diff --git a/Directory.Packages.props b/Directory.Packages.props index 9d70c1482a..36ed2f263d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -175,4 +175,4 @@ - + \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json index 8470709c99..44ab5b3e31 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json @@ -12,6 +12,7 @@ "CommunityWebSite": "ABP community website", "ManageAccount": "My Account | ABP.IO", "ManageYourProfile": "Manage your profile", - "ReturnToApplication": "Return to application" + "ReturnToApplication": "Return to application", + "IdentityUserNotAvailable:Deleted": "This email address is not available. Reason: Already deleted." } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json index 5be67a44b1..cfaa25b11b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json @@ -816,6 +816,7 @@ "ErrorExceptionMessage": "حدث خطأ أثناء معالجة طلبك", "WatchTakeCodeGeneration": "شاهد فيديو \"استكشاف إمكانات إنشاء الأكواد البرمجية: ABP Suite\"!", "ExtendNow": "تمديد / تجديد", - "RegisterDemo": "يسجل" + "RegisterDemo": "يسجل", + "AbpStudio_ComingSoon": "إذا كنت ترغب في اختبار ABP Studio قبل أي شخص آخر، يمكنك أن تصبح أحد مختبري النسخة التجريبية" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json index c047dd7b65..e2739874d4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json @@ -431,6 +431,7 @@ "ErrorExceptionMessage": "Při zpracování vašeho požadavku nastala chyba", "WatchTakeCodeGeneration": "Podívejte se na video „Prozkoumejte potenciál generování kódu: ABP Suite“!", "ExtendNow": "Prodloužit / Obnovit", - "RegisterDemo": "Registrovat" + "RegisterDemo": "Registrovat", + "AbpStudio_ComingSoon": "Pokud chcete ABP Studio otestovat dříve než kdokoli jiný, staňte se BETA testerem" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json index 1663811cef..f6cd58c44c 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json @@ -411,6 +411,7 @@ "SendUsEmail": "Senden Sie uns eine E-Mail", "ErrorExceptionMessage": "Während Ihrer Anfrage ist ein Fehler aufgetreten", "WatchTakeCodeGeneration": "Sehen Sie sich das Video „Entdecken Sie das Potenzial der Codegenerierung: ABP Suite“ an!", - "ExtendNow": "Verlängern / Erneuern" + "ExtendNow": "Verlängern / Erneuern", + "AbpStudio_ComingSoon": "Wenn Sie ABP Studio vor allen anderen testen möchten, werden Sie BETA-Tester" } } \ 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 3fc2d697ee..ebfe8e1583 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -916,7 +916,7 @@ "AbpStudio_Description": "ABP Studio is still under development. You can fill out the form below to be one of the first users.", "AbpStudio_Description1": "ABP Studio is a cross-platform desktop application for ABP developers.", "AbpStudio_Description2": "It is well integrated to the ABP Framework and aims to provide a comfortable development environment for you by automating things, providing insights about your solution, making develop, run and deploy your solutions much easier.", - "AbpStudio_ComingSoon": "Coming Soon Planned beta release date: Q4 of 2023.", + "AbpStudio_ComingSoon": "If you want to test ABP Studio before anyone else, become a BETA tester", "AbpStudio_PlannedPreviewDate": "Planned preview release date: Q4 of 2023.", "BetaRequest": "Beta Request", "CreateNewSolutions": "Create New Solutions", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json index 3f54749131..1aa1a86033 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json @@ -411,6 +411,7 @@ "SendUsEmail": "Envíanos un correo electrónico", "ErrorExceptionMessage": "Se produjo un error al procesar su solicitud.", "WatchTakeCodeGeneration": "¡Mire el vídeo \"Explore el potencial de la generación de código: ABP Suite\"!", - "ExtendNow": "Ampliar / Renovar" + "ExtendNow": "Ampliar / Renovar", + "AbpStudio_ComingSoon": "Si quieres probar ABP Studio antes que nadie, conviértete en tester BETA" } } \ 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 f367f63c30..2ebacacf2b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json @@ -852,6 +852,7 @@ "ErrorExceptionMessage": "Virhe pyynnön käsittelyn yhteydessä", "WatchTakeCodeGeneration": "Katso \"Tutki koodin luomisen potentiaalia: ABP Suite\" -video!", "ExtendNow": "Laajenna / Uusi", - "RegisterDemo": "Rekisteröidy" + "RegisterDemo": "Rekisteröidy", + "AbpStudio_ComingSoon": "Jos haluat testata ABP Studiota ennen muita, ryhdy BETA-testaajaksi" } } \ 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 b044837aac..da1b6fb0b9 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json @@ -431,6 +431,7 @@ "ErrorExceptionMessage": "Une erreur s'est produite lors du traitement de votre demande", "WatchTakeCodeGeneration": "Regardez la vidéo « Explorez le potentiel de la génération de code : ABP Suite » !", "ExtendNow": "Prolonger / Renouveler", - "RegisterDemo": "Registre" + "RegisterDemo": "Registre", + "AbpStudio_ComingSoon": "Si vous souhaitez tester ABP Studio avant tout le monde, devenez BETA testeur" } } \ 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 958e3d533f..d778e566f4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json @@ -430,6 +430,7 @@ "ErrorExceptionMessage": "आपका अनुरोध संसाधित करते समय एक त्रुटि पाई गई", "WatchTakeCodeGeneration": "\"कोड जनरेशन की क्षमता का अन्वेषण करें: एबीपी सुइट\" वीडियो देखें!", "ExtendNow": "विस्तार/नवीनीकरण", - "RegisterDemo": "पंजीकरण करवाना" + "RegisterDemo": "पंजीकरण करवाना", + "AbpStudio_ComingSoon": "यदि आप किसी और से पहले एबीपी स्टूडियो का परीक्षण करना चाहते हैं, तो बीटा परीक्षक बनें" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json index 6f8c1930cf..ccaec17329 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json @@ -915,7 +915,6 @@ "AbpStudio_Description": "ABP Studio je još uvijek u razvoju. Možete ispuniti obrazac ispod i biti jedan od prvih korisnika.", "AbpStudio_Description1": "ABP Studio je stolna aplikacija za više platformi za ABP programere.", "AbpStudio_Description2": "Dobro je integriran u ABP Framework i ima za cilj pružiti vam ugodno razvojno okruženje automatiziranjem stvari, pružanjem uvida u vaše rješenje, čineći razvoj, pokretanje i implementaciju vaših rješenja puno lakšim.", - "AbpStudio_ComingSoon": "Uskoro. Planirani datum izdavanja beta verzije: 4. tromjesečje 2023.", "AbpStudio_PlannedPreviewDate": "Planirani datum izdavanja pregleda: 4. kvartal 2023.", "BetaRequest": "Zahtjev za beta verziju", "CreateNewSolutions": "Stvorite nova rješenja", @@ -1107,6 +1106,7 @@ "SendUsEmail": "Pošaljite nam e-mail", "ErrorExceptionMessage": "Pojavila se greška prilikom obrade Vašeg zahtjeva", "WatchTakeCodeGeneration": "Pogledajte video \"Istražite potencijal generiranja koda: ABP Suite\"!", - "ExtendNow": "Produži / obnovi" + "ExtendNow": "Produži / obnovi", + "AbpStudio_ComingSoon": "Ako želite testirati ABP Studio prije svih ostalih, postanite BETA tester" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json index 79889c0d1b..5584f68f7d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json @@ -777,6 +777,7 @@ "SendUsEmail": "Küldjön nekünk e-mailt", "ErrorExceptionMessage": "Hiba történt a kérése feldolgozása során", "WatchTakeCodeGeneration": "Nézze meg a \"Fedezze fel a kódgenerálás lehetőségét: ABP Suite\" videót!", - "ExtendNow": "Bővítés / Megújítás" + "ExtendNow": "Bővítés / Megújítás", + "AbpStudio_ComingSoon": "Ha bárki más előtt szeretné tesztelni az ABP Studio-t, legyen BÉTA tesztelő" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json index d6744a9603..09b66636d0 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json @@ -408,6 +408,7 @@ "SendUsEmail": "Sendu okkur tölvupóst", "ErrorExceptionMessage": "Villa kom upp við úrvinnslu beiðni þinnar", "WatchTakeCodeGeneration": "Horfðu á \"Kannaðu möguleika kóðaframleiðslu: ABP Suite\" myndbandið!", - "ExtendNow": "Framlengja / endurnýja" + "ExtendNow": "Framlengja / endurnýja", + "AbpStudio_ComingSoon": "Ef þú vilt prófa ABP Studio á undan öllum öðrum skaltu gerast BETA prófari" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json index addb4b54e1..88658fb5b5 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json @@ -430,6 +430,7 @@ "ErrorExceptionMessage": "Si è verificato un errore durante l'elaborazione della richiesta", "WatchTakeCodeGeneration": "Guarda il video \"Esplora il potenziale della generazione di codice: ABP Suite\"!", "ExtendNow": "Estendi/Rinnova", - "RegisterDemo": "Registrati" + "RegisterDemo": "Registrati", + "AbpStudio_ComingSoon": "Se vuoi provare ABP Studio prima di chiunque altro, diventa un BETA tester" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json index 298e59d573..23bce5d1d6 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json @@ -408,6 +408,7 @@ "SendUsEmail": "Stuur ons een e-mail", "ErrorExceptionMessage": "Er is een fout opgetreden bij het verwerken van uw verzoek", "WatchTakeCodeGeneration": "Bekijk de video 'Ontdek het potentieel van codegeneratie: ABP Suite'!", - "ExtendNow": "Verlengen / Verlengen" + "ExtendNow": "Verlengen / Verlengen", + "AbpStudio_ComingSoon": "Als u ABP Studio als eerste wilt testen, wordt dan BETA-tester" } } \ No newline at end of file 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 757fc93ec1..0f33ee2a9d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json @@ -409,6 +409,7 @@ "ErrorExceptionMessage": "Podczas przetwarzania żądania wystąpił błąd", "WatchTakeCodeGeneration": "Obejrzyj film „Odkryj potencjał generowania kodu: pakiet ABP”!", "ExtendNow": "Przedłuż/Odnów", - "RegisterDemo": "Rejestr" + "RegisterDemo": "Rejestr", + "AbpStudio_ComingSoon": "Jeśli chcesz przetestować ABP Studio zanim ktokolwiek inny, zostań testerem BETA" } } \ No newline at end of file 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 fe72c04d15..b72319477f 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json @@ -431,6 +431,7 @@ "SendUsEmail": "Envie-nos um e-mail", "ErrorExceptionMessage": "Um erro ocorreu durante o processamento do seu pedido", "WatchTakeCodeGeneration": "Assista ao vídeo \"Explore o potencial de geração de código: ABP Suite\"!", - "ExtendNow": "Estender/Renovar" + "ExtendNow": "Estender/Renovar", + "AbpStudio_ComingSoon": "Se você quiser testar o ABP Studio antes de todo mundo, torne-se um testador BETA" } } \ No newline at end of file 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 6bb638c703..275c2c5672 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json @@ -408,6 +408,7 @@ "SendUsEmail": "Trimite-ne e-mail", "ErrorExceptionMessage": "A apărut o eroare în timpul procesării cererii dumneavoastră", "WatchTakeCodeGeneration": "Urmărește videoclipul „Explorați potențialul generării codului: ABP Suite”!", - "ExtendNow": "Extinde/Reînnoiește" + "ExtendNow": "Extinde/Reînnoiește", + "AbpStudio_ComingSoon": "Dacă doriți să testați ABP Studio înaintea oricui, deveniți un tester BETA" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json index 26f1c79704..acaef4b285 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json @@ -427,6 +427,7 @@ "SendUsEmail": "Pošlite nám e-mail", "ErrorExceptionMessage": "Nastala chyba pri spracovaní vašej žiadosti", "WatchTakeCodeGeneration": "Pozrite si video „Preskúmajte potenciál generovania kódu: ABP Suite“!", - "ExtendNow": "Predĺžiť / obnoviť" + "ExtendNow": "Predĺžiť / obnoviť", + "AbpStudio_ComingSoon": "Ak chcete otestovať ABP Studio skôr ako ktokoľvek iný, staňte sa BETA testerom" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json index 0d25e94113..d71700abad 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json @@ -409,6 +409,7 @@ "ErrorExceptionMessage": "Pri obdelavi vaše zahteve je prišlo do napake", "WatchTakeCodeGeneration": "Oglejte si video »Raziščite potencial generiranja kode: zbirka ABP«!", "ExtendNow": "Podaljšaj / obnovi", - "RegisterDemo": "Registrirajte se" + "RegisterDemo": "Registrirajte se", + "AbpStudio_ComingSoon": "Če želite preizkusiti ABP Studio pred vsemi drugimi, postanite BETA tester" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json index 9282239bee..1f77dd3f25 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json @@ -1058,7 +1058,6 @@ "AbpStudio_Description": "ABP Studio hala geliştirme aşamasında. İlk kullanıcılardan biri olmak için aşağıdaki formu doldurabilirsiniz.", "AbpStudio_Description1": "ABP Studio, ABP geliştiricileri için çok platformlu masaüstü uygulamasıdır.", "AbpStudio_Description2": "ABP Studio, ABP Framework'e iyi entegre edilmiştir ve sizin için işleri otomatikleştirerek, projeniz hakkında bilgi sağlayarak, projelerinizi geliştirmeyi, çalıştırmayı ve dağıtımını çok daha kolay hale getirmeyi amaçlamaktadır.", - "AbpStudio_ComingSoon": "Yakında Planlanan beta sürüm tarihi: 2023'ün 4. çeyreği.", "AbpStudio_PlannedPreviewDate": "Planlanan beta sürüm tarihi: 2023'ün 4. çeyreği.", "BetaRequest": "Beta Talebi", "CreateNewSolutions": "Yeni Projeler Oluştur", @@ -1140,6 +1139,7 @@ "RemoveBasket": "Sepetten kaldır", "TrainingPack": "Eğitim paketi", "TrainingPackDiscount": "Eğitim paketi indirimi", - "LinkExpiredMessage": "Ödeme bağlantısının süresi doldu! Bağlantıyı güncellemek için sales@volosoft.com adresinden bizimle iletişime geçin veya iletişim sayfasına gitmek için buraya tıklayın." + "LinkExpiredMessage": "Ödeme bağlantısının süresi doldu! Bağlantıyı güncellemek için sales@volosoft.com adresinden bizimle iletişime geçin veya iletişim sayfasına gitmek için buraya tıklayın.", + "AbpStudio_ComingSoon": "ABP Studio'yu herkesten önce test etmek istiyorsanız BETA test kullanıcısı olun" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json index 6834f26f24..f4bd23a37e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json @@ -408,6 +408,7 @@ "SendUsEmail": "Gửi email cho chúng tôi", "ErrorExceptionMessage": "Đã xảy ra lỗi trong khi xử lý yêu cầu của bạn", "WatchTakeCodeGeneration": "Xem video \"Khám phá tiềm năng tạo mã: ABP Suite\"!", - "ExtendNow": "Gia hạn/Gia hạn" + "ExtendNow": "Gia hạn/Gia hạn", + "AbpStudio_ComingSoon": "Nếu bạn muốn thử nghiệm ABP Studio trước bất kỳ ai khác, hãy trở thành người thử nghiệm BETA" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json index 57caf8322d..f86187170b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json @@ -915,7 +915,6 @@ "AbpStudio_Description": "ABP Studio 仍在开发中。您可以填写下面的表单,成为首批用户之一。", "AbpStudio_Description1": "ABP Studio 是专为 ABP 开发人员设计的跨平台桌面应用程序。", "AbpStudio_Description2": "它与 ABP 框架集成得很好,旨在通过自动化、提供有关解决方案的见解,为您提供舒适的开发环境,使开发、运行部署解决方案变得更加容易。", - "AbpStudio_ComingSoon": "即将发布 计划测试版发布日期:计划测试版发布日期:2023年第四季度。", "AbpStudio_PlannedPreviewDate": "计划预览版发布日期:2023 年第四季度。", "BetaRequest": "测试版请求", "CreateNewSolutions": "创建新的解决方案", @@ -1110,6 +1109,7 @@ "ErrorExceptionMessage": "处理您的请求时发生错误", "WatchTakeCodeGeneration": "观看“探索代码生成的潜力:ABP Suite”视频!", "PreBuiltApplicationModulesTitle": "预构建应用程序 模块", - "RegisterDemo": "登记" + "RegisterDemo": "登记", + "AbpStudio_ComingSoon": "如果您想先于其他人测试 ABP Studio,请成为 BETA 测试员" } } 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 e9ee4ef230..3b4d8bc88b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json @@ -897,7 +897,6 @@ "AbpStudio_Description": "ABP Studio 仍在開發中。您可以填寫下面的表单,成為第一批使用者之一。", "AbpStudio_Description1": "ABP Studio 是一款針對 ABP 開發人員的跨平台桌面應用程式。", "AbpStudio_Description2": "它與 ABP 框架很好地集成,旨在透過自動化為您提供一個舒適的開發環境,提供有關您的解決方案的見解,使開發、運行部署您的解決方案變得更加容易。", - "AbpStudio_ComingSoon": "即將推出計劃測試版發布日期:2023 年第四季。", "AbpStudio_PlannedPreviewDate": "計畫預覽版發布日期:2023 年第四季。", "BetaRequest": "測試版請求", "CreateNewSolutions": "建立新的解決方案", @@ -1100,6 +1099,7 @@ "WatchTakeCodeGeneration": "观看“探索代码生成的潜力:ABP Suite”视频!", "ExtendNow": "延长 / 续订", "PreBuiltApplicationModulesTitle": "预构建应用程序 模块", - "RegisterDemo": "登记" + "RegisterDemo": "登记", + "AbpStudio_ComingSoon": "如果您想先于其他人测试 ABP Studio,请成为 BETA 测试员" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json index 2b6f5db40c..74a011fb40 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown أيد.", "Preview": "معاينة", "VisitVideoCourseDescription": "إذا كنت ترغب في تعلم أساسيات إطار عمل برنامج ABP، فاطلع على دورات الفيديو الخاصة ببرنامج ABP Essentials.", - "VisitPage": "زر الصفحة" + "VisitPage": "زر الصفحة", + "ConfirmEmailForPost": "لتتمكن من النشر، تحتاج إلى تأكيد بريدك الإلكتروني. انتقل إلى account.abp.io/Account/Manage وتحقق من بريدك الإلكتروني في علامة التبويب \"المعلومات الشخصية\".", + "DailyPostCreateLimitation": "لقد وصلت إلى الحد الأقصى اليومي لإنشاء المنشورات. يمكنك إنشاء مشاركة جديدة في {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json index 35119a1408..86910d5557 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown podporováno.", "Preview": "Náhled", "VisitPage": "Navštivte stránku", - "VisitVideoCourseDescription": "Pokud se chcete naučit základy rámce ABP, podívejte se na videokurzy ABP Essentials." + "VisitVideoCourseDescription": "Pokud se chcete naučit základy rámce ABP, podívejte se na videokurzy ABP Essentials.", + "ConfirmEmailForPost": "Abyste mohli přidávat příspěvky, musíte potvrdit svůj e-mail. Přejděte na stránku account.abp.io/Account/Manage a ověřte svůj e-mail na kartě Osobní údaje.", + "DailyPostCreateLimitation": "Dosáhli jste denního limitu pro vytváření příspěvků. Nový příspěvek můžete vytvořit v {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json index d26c081de5..99c60d5199 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown unterstützt.", "Preview": "Vorschau", "VisitPage": "Seite besuchen", - "VisitVideoCourseDescription": "Wenn Sie die Grundlagen des ABP Framework erlernen möchten, schauen Sie sich die ABP Essentials-Videokurse an." + "VisitVideoCourseDescription": "Wenn Sie die Grundlagen des ABP Framework erlernen möchten, schauen Sie sich die ABP Essentials-Videokurse an.", + "ConfirmEmailForPost": "Um Beiträge verfassen zu können, müssen Sie Ihre E-Mail-Adresse bestätigen. Gehen Sie zu account.abp.io/Account/Manage und bestätigen Sie Ihre E-Mail-Adresse auf der Registerkarte „Persönliche Daten“.", + "DailyPostCreateLimitation": "Sie haben das tägliche Limit für die Erstellung von Beiträgen erreicht. Sie können in {0} einen neuen Beitrag erstellen." } } \ 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 80fbc8539b..d2e7f9b6a8 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -261,6 +261,8 @@ "Preview": "Preview", "VisitPage": "Visit Page", "VisitVideoCourseDescription": "If you want to learn the basics of the ABP Framework, check out the ABP Essentials Video courses.", - "EditProfile": "Edit Profile" + "EditProfile": "Edit Profile", + "ConfirmEmailForPost": "To be able to post, you need to confirm your email. Go to account.abp.io/Account/Manage and verify your email in the Personal Info tab.", + "DailyPostCreateLimitation": "You have reached the daily post creation limit. You can create a new post in {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json index bf045dea81..2403cb7681 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown soportado.", "Preview": "Avance", "VisitPage": "Visita la página", - "VisitVideoCourseDescription": "Si desea aprender los conceptos básicos del marco ABP, consulte los cursos en vídeo de ABP Essentials." + "VisitVideoCourseDescription": "Si desea aprender los conceptos básicos del marco ABP, consulte los cursos en vídeo de ABP Essentials.", + "ConfirmEmailForPost": "Para poder publicar, debe confirmar su correo electrónico. Vaya a account.abp.io/Account/Manage y verifique su correo electrónico en la pestaña Información personal.", + "DailyPostCreateLimitation": "Has alcanzado el límite diario de creación de publicaciones. Puedes crear una nueva publicación en {0}." } } \ 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 acf70381ff..3f420b1822 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown tuettu.", "Preview": "Esikatselu", "VisitPage": "Vieraile sivulla", - "VisitVideoCourseDescription": "Jos haluat oppia ABP Frameworkin perusteet, katso ABP Essentials Video -kurssit." + "VisitVideoCourseDescription": "Jos haluat oppia ABP Frameworkin perusteet, katso ABP Essentials Video -kurssit.", + "ConfirmEmailForPost": "Jotta voit lähettää viestejä, sinun on vahvistettava sähköpostiosoitteesi. Siirry osoitteeseen account.abp.io/Account/Manage ja vahvista sähköpostiosoitteesi Henkilökohtaiset tiedot -välilehdessä.", + "DailyPostCreateLimitation": "Olet saavuttanut päivittäisen viestien luomisrajan. Voit luoda uuden viestin kohteessa {0}." } } \ 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 963bde3048..a7821015b4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown prise en charge.", "Preview": "Aperçu", "VisitPage": "Page de visite", - "VisitVideoCourseDescription": "Si vous souhaitez apprendre les bases du framework ABP, consultez les cours vidéo ABP Essentials." + "VisitVideoCourseDescription": "Si vous souhaitez apprendre les bases du framework ABP, consultez les cours vidéo ABP Essentials.", + "ConfirmEmailForPost": "Pour pouvoir publier, vous devez confirmer votre e-mail. Accédez à account.abp.io/Account/Manage et vérifiez votre e-mail dans l'onglet Informations personnelles.", + "DailyPostCreateLimitation": "Vous avez atteint la limite quotidienne de création de publications. Vous pouvez créer une nouvelle publication dans {0}." } } \ 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 940d92573f..87919eef8b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown का समर्थन किया।", "Preview": "पूर्व दर्शन", "VisitPage": "यात्रा पेज", - "VisitVideoCourseDescription": "यदि आप एबीपी फ्रेमवर्क की मूल बातें सीखना चाहते हैं, तो एबीपी एसेंशियल वीडियो पाठ्यक्रम देखें।" + "VisitVideoCourseDescription": "यदि आप एबीपी फ्रेमवर्क की मूल बातें सीखना चाहते हैं, तो एबीपी एसेंशियल वीडियो पाठ्यक्रम देखें।", + "ConfirmEmailForPost": "पोस्ट करने में सक्षम होने के लिए, आपको अपने ईमेल की पुष्टि करनी होगी। account.abp.io/Account/Manage पर जाएं और व्यक्तिगत जानकारी टैब में अपना ईमेल सत्यापित करें।", + "DailyPostCreateLimitation": "आप दैनिक पोस्ट निर्माण सीमा तक पहुंच गए हैं। आप {0} में एक नई पोस्ट बना सकते हैं।" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json index f0b3a5941c..5cb7ff44a0 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown podržan.", "Preview": "Pregled", "VisitPage": "Posjetite stranicu", - "VisitVideoCourseDescription": "Ako želite naučiti osnove ABP okvira, pogledajte video tečajeve ABP Essentials." + "VisitVideoCourseDescription": "Ako želite naučiti osnove ABP okvira, pogledajte video tečajeve ABP Essentials.", + "ConfirmEmailForPost": "Da biste mogli objavljivati, morate potvrditi svoju e-poštu. Idite na account.abp.io/Account/Manage i potvrdite svoju e-poštu na kartici Osobni podaci.", + "DailyPostCreateLimitation": "Dosegli ste dnevno ograničenje za izradu postova. Možete stvoriti novi post u {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json index 84117ededd..95433c6d92 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown támogatott.", "Preview": "Előnézet", "VisitPage": "Látogassa meg az oldalt", - "VisitVideoCourseDescription": "Ha meg szeretné tanulni az ABP Framework alapjait, nézze meg az ABP Essentials Video tanfolyamokat." + "VisitVideoCourseDescription": "Ha meg szeretné tanulni az ABP Framework alapjait, nézze meg az ABP Essentials Video tanfolyamokat.", + "ConfirmEmailForPost": "A bejegyzések közzétételéhez meg kell erősítenie e-mail-címét. Nyissa meg az account.abp.io/Account/Manage oldalt, és ellenőrizze e-mail-címét a Személyes adatok lapon.", + "DailyPostCreateLimitation": "Elérte a napi bejegyzéslétrehozási korlátot. Új bejegyzést itt hozhat létre: {0}." } } \ 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 953186b239..8f2a733a2d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown stutt.", "Preview": "Forskoðun", "VisitPage": "Heimsæktu síðu", - "VisitVideoCourseDescription": "Ef þú vilt læra grunnatriði ABP Framework skaltu skoða ABP Essentials Video námskeiðin." + "VisitVideoCourseDescription": "Ef þú vilt læra grunnatriði ABP Framework skaltu skoða ABP Essentials Video námskeiðin.", + "ConfirmEmailForPost": "Til að geta sent færslur þarftu að staðfesta netfangið þitt. Farðu á account.abp.io/Account/Manage og staðfestu tölvupóstinn þinn á Persónulegum upplýsingum flipanum.", + "DailyPostCreateLimitation": "Þú hefur náð daglegu takmörkunum fyrir færslu færslu. Þú getur búið til nýja færslu í {0}." } } \ 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 88bf43fc0e..45021c315d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown supportato.", "Preview": "Anteprima", "VisitPage": "Visita la pagina", - "VisitVideoCourseDescription": "Se vuoi apprendere le nozioni di base del Framework ABP, dai un'occhiata ai corsi video ABP Essentials." + "VisitVideoCourseDescription": "Se vuoi apprendere le nozioni di base del Framework ABP, dai un'occhiata ai corsi video ABP Essentials.", + "ConfirmEmailForPost": "Per poter pubblicare, devi confermare la tua email. Vai su account.abp.io/Account/Manage e verifica la tua email nella scheda Informazioni personali.", + "DailyPostCreateLimitation": "Hai raggiunto il limite giornaliero di creazione di post. Puoi creare un nuovo post in {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json index 5d18bde830..958c68263d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown ondersteund.", "Preview": "Voorbeeld", "VisitPage": "Bezoek pagina", - "VisitVideoCourseDescription": "Als je de basis van het ABP Framework wilt leren, bekijk dan de ABP Essentials Videocursussen." + "VisitVideoCourseDescription": "Als je de basis van het ABP Framework wilt leren, bekijk dan de ABP Essentials Videocursussen.", + "ConfirmEmailForPost": "Om te kunnen posten, moet u uw e-mailadres bevestigen. Ga naar account.abp.io/Account/Manage en verifieer uw e-mailadres op het tabblad Persoonlijke informatie.", + "DailyPostCreateLimitation": "Je hebt de dagelijkse limiet voor het maken van berichten bereikt. Je kunt een nieuw bericht maken in {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json index a86b93515c..16af07e6e4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown utrzymany.", "Preview": "Zapowiedź", "VisitPage": "Odwiedź stronę", - "VisitVideoCourseDescription": "Jeśli chcesz poznać podstawy ABP Framework, sprawdź kursy wideo ABP Essentials." + "VisitVideoCourseDescription": "Jeśli chcesz poznać podstawy ABP Framework, sprawdź kursy wideo ABP Essentials.", + "ConfirmEmailForPost": "Aby móc publikować, musisz potwierdzić swój adres e-mail. Przejdź do account.abp.io/Account/Manage i zweryfikuj swój adres e-mail w zakładce Dane osobowe.", + "DailyPostCreateLimitation": "Osiągnąłeś dzienny limit tworzenia postów. Możesz utworzyć nowy post w {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json index af28a46228..249e758a53 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown suportado.", "Preview": "Visualização", "VisitPage": "Visite a página", - "VisitVideoCourseDescription": "Se você quiser aprender o básico do ABP Framework, confira os cursos em vídeo ABP Essentials." + "VisitVideoCourseDescription": "Se você quiser aprender o básico do ABP Framework, confira os cursos em vídeo ABP Essentials.", + "ConfirmEmailForPost": "Para poder postar, você precisa confirmar seu e-mail. Acesse account.abp.io/Account/Manage e verifique seu e-mail na guia Informações pessoais.", + "DailyPostCreateLimitation": "Você atingiu o limite diário de criação de postagens. Você pode criar uma nova postagem em {0}." } } \ 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 e9d89b398b..7d33ecdb90 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown sprijinit.", "Preview": "previzualizare", "VisitPage": "Vizitați pagina", - "VisitVideoCourseDescription": "Dacă doriți să aflați elementele de bază ale cadrului ABP, consultați cursurile video ABP Essentials." + "VisitVideoCourseDescription": "Dacă doriți să aflați elementele de bază ale cadrului ABP, consultați cursurile video ABP Essentials.", + "ConfirmEmailForPost": "Pentru a putea posta, trebuie să vă confirmați adresa de e-mail. Accesați account.abp.io/Account/Manage și verificați e-mailul în fila Informații personale.", + "DailyPostCreateLimitation": "Ați atins limita zilnică de creare a postărilor. Puteți crea o postare nouă în {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json index dcfbc84bf9..6c842ef388 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown podporované.", "Preview": "Náhľad", "VisitPage": "Navštívte stránku", - "VisitVideoCourseDescription": "Ak sa chcete naučiť základy rámca ABP, pozrite si video kurzy ABP Essentials." + "VisitVideoCourseDescription": "Ak sa chcete naučiť základy rámca ABP, pozrite si video kurzy ABP Essentials.", + "ConfirmEmailForPost": "Aby ste mohli uverejňovať príspevky, musíte potvrdiť svoj e-mail. Prejdite na stránku account.abp.io/Account/Manage a overte svoj e-mail na karte Osobné informácie.", + "DailyPostCreateLimitation": "Dosiahli ste denný limit na vytváranie príspevkov. Nový príspevok môžete vytvoriť v {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json index 8ed4d53271..eb6a616017 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown podprt.", "Preview": "Predogled", "VisitPage": "Obiščite stran", - "VisitVideoCourseDescription": "Če se želite naučiti osnov ogrodja ABP, si oglejte video tečaje ABP Essentials." + "VisitVideoCourseDescription": "Če se želite naučiti osnov ogrodja ABP, si oglejte video tečaje ABP Essentials.", + "ConfirmEmailForPost": "Če želite objavljati, morate potrditi svoj e-poštni naslov. Pojdite na account.abp.io/Account/Manage in potrdite svoj e-poštni naslov na zavihku Osebni podatki.", + "DailyPostCreateLimitation": "Dosegli ste dnevno omejitev za ustvarjanje objav. Novo objavo lahko ustvarite v {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json index 711d068087..14d6207c79 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json @@ -244,6 +244,8 @@ "BuyTicket": "Bilet al", "SeeEvent": "Etkinliği Gör", "Cancel": "Vazgeç", - "Continue": "Devam" + "Continue": "Devam", + "ConfirmEmailForPost": "Gönderi paylaşabilmek için e-posta adresinizi onaylamanız gerekir. account.abp.io/Account/Manage adresine gidin ve Kişisel Bilgiler sekmesinden e-posta adresinizi doğrulayın.", + "DailyPostCreateLimitation": "Günlük gönderi paylaşma sınırına ulaştınız. {0}'da yeni bir gönderi paylaşabilirsiniz." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json index bd6baa67bb..4ca0c8ca49 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown được hỗ trợ.", "Preview": "Xem trước", "VisitPage": "Ghé thăm trang", - "VisitVideoCourseDescription": "Nếu bạn muốn tìm hiểu những kiến ​​thức cơ bản về Khung ABP, hãy xem các khóa học Video Cơ bản về ABP." + "VisitVideoCourseDescription": "Nếu bạn muốn tìm hiểu những kiến ​​thức cơ bản về Khung ABP, hãy xem các khóa học Video Cơ bản về ABP.", + "ConfirmEmailForPost": "Để có thể đăng bài, bạn cần xác nhận email của mình. Hãy truy cập account.abp.io/Account/Quản lý và xác minh email của bạn trong tab Thông tin cá nhân.", + "DailyPostCreateLimitation": "Bạn đã đạt đến giới hạn tạo bài đăng hàng ngày. Bạn có thể tạo bài đăng mới trong {0}." } } \ 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 2bf0e9fb50..c6ef5a2c3d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown 支持的。", "Preview": "预览", "VisitPage": "访问页面", - "VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。" + "VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。", + "ConfirmEmailForPost": "为了能够发帖,您需要确认您的电子邮件。转到 account.abp.io/Account/Manage 并在“个人信息”选项卡中验证您的电子邮件。", + "DailyPostCreateLimitation": "您已达到每日帖子创建限制。您可以在 {0} 中创建新帖子。" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json index 61bb0aa8a0..b72c927296 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown 支持的。", "Preview": "预览", "VisitPage": "访问页面", - "VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。" + "VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。", + "ConfirmEmailForPost": "为了能够发帖,您需要确认您的电子邮件。转到 account.abp.io/Account/Manage 并在“个人信息”选项卡中验证您的电子邮件。", + "DailyPostCreateLimitation": "您已达到每日帖子创建限制。您可以在 {0} 中创建新帖子。" } } \ No newline at end of file diff --git a/common.props b/common.props index fbcd32b20b..2b87badaf8 100644 --- a/common.props +++ b/common.props @@ -1,8 +1,8 @@ latest - 8.2.0-rc.3 - 3.2.0-rc.3 + 8.3.0-preview + 3.3.0-preview $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/abp-studio-solution-runner.png b/docs/en/Blog-Posts/2024-05 ABP Unification/abp-studio-solution-runner.png new file mode 100644 index 0000000000..2b5bc20678 Binary files /dev/null and b/docs/en/Blog-Posts/2024-05 ABP Unification/abp-studio-solution-runner.png differ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/cover.png b/docs/en/Blog-Posts/2024-05 ABP Unification/cover.png new file mode 100644 index 0000000000..95e8363e67 Binary files /dev/null and b/docs/en/Blog-Posts/2024-05 ABP Unification/cover.png differ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/docs-new-navigation.png b/docs/en/Blog-Posts/2024-05 ABP Unification/docs-new-navigation.png new file mode 100644 index 0000000000..ba1f421339 Binary files /dev/null and b/docs/en/Blog-Posts/2024-05 ABP Unification/docs-new-navigation.png differ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/docs-project-selection.png b/docs/en/Blog-Posts/2024-05 ABP Unification/docs-project-selection.png new file mode 100644 index 0000000000..2e4dadafc6 Binary files /dev/null and b/docs/en/Blog-Posts/2024-05 ABP Unification/docs-project-selection.png differ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/new-get-started.png b/docs/en/Blog-Posts/2024-05 ABP Unification/new-get-started.png new file mode 100644 index 0000000000..ed1483f343 Binary files /dev/null and b/docs/en/Blog-Posts/2024-05 ABP Unification/new-get-started.png differ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/new-mega-menu.png b/docs/en/Blog-Posts/2024-05 ABP Unification/new-mega-menu.png new file mode 100644 index 0000000000..75c21a9693 Binary files /dev/null and b/docs/en/Blog-Posts/2024-05 ABP Unification/new-mega-menu.png differ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/new-pricing.png b/docs/en/Blog-Posts/2024-05 ABP Unification/new-pricing.png new file mode 100644 index 0000000000..4d299da6ee Binary files /dev/null and b/docs/en/Blog-Posts/2024-05 ABP Unification/new-pricing.png differ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/post.md b/docs/en/Blog-Posts/2024-05 ABP Unification/post.md new file mode 100644 index 0000000000..9da50ac06b --- /dev/null +++ b/docs/en/Blog-Posts/2024-05 ABP Unification/post.md @@ -0,0 +1,111 @@ +# Unifying the ABP.IO Platform + +I am very excited to announce that some big changes and improvements are coming to the ABP.IO Platform soon. In this post, I will explain the changes we are currently working on. Here, a brief list of these changes: + +* We are merging the subdomains of the ABP.IO Platform websites: Community.abp.io, commercial.abp.io, blog.abp.io, docs.abp.io websites and their contents are being merged into the main domain, abp.io. +* ABP (open source) and ABP Commercial documents are being merged into a single documentation. +* Introducing ABP Studio Community Edition. + +These changes won't effect the license conditions. The open source part will remain the same and the commercial license contents will also be the same. The aim of the changes is to make the platform more consistent, holistic, understandable and easy to start. + +Let's dive deep... + +## Merging the ABP.IO Websites + +ABP.IO website has many subdomains currently: + +* **abp.io**: Home page of the open source ABP Framework project. +* **community.abp.io**: A website that community can share contents and we organize events. +* **commercial.abp.io**: A website to promote and sell commercial ABP licenses which have pre-built modules, themes, tooling and support on top of the ABP Framework. +* **docs.abp.io**: The technical documentation of the ABP Framework and ABP Commercial. +* **blog.abp.io**: A blog website to announce the news on the platform. +* **support.abp.io**: Premium support for the ABP Commercial customers. + +All these subdomains (except the support website for now) are being merged to the abp.io domain. All their contents and UI designs are being revised and enriched. + +Some fundamental purposes of that change are; + +* Making content more coherent and holistic, +* Making the design more harmonious, +* Making the contents of the old subdomains more visible and reachable, +* Allow you to navigate through the web pages much easier, +* Reducing duplications between different websites, + +I will highlight a few important changes in the next sections. + +### The New Mega Menu + +As I said above, the abp.io UI design is also being revised. One of the big revisions is the main menu. We are replacing the current main navigation by a mega menu as shown in the following figure: + +![new-mega-menu](new-mega-menu.png) + +We believe that new mega menu will allow you to navigate through the web pages much easier. + +### The New Get Started Page + +We are constantly working to improve ABP's onboarding experience. With the new platform changes, we now offer ABP Studio as the starting point for the ABP Platform. You can still use the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) to created new ABP solutions, but the new ABP Studio makes it much easier and understandable. It also provides features to easily run and monitor your applications, even in the Community edition. + +![new-get-started](new-get-started.png) + +You can easily download and install ABP Studio, login with your abp.io account and create your first ABP solution. + +### The New Pricing Page + +Since the [ABP Commercial website](https://commercial.abp.io/) has merged with the main website, you will see the *Pricing* page located on the main menu of the abp.io website. We have completely revised the design and content of this page to better reflect which features are open source and free, and what is included in the paid licenses. + +![new-pricing](new-pricing.png) + +As mentioned above, all the free & open source features are still free & open source. In addition, we included the ABP Studio Community edition (will be explained below) to the free license. + +## Merging the ABP Platform Documentation + +Currently, ABP Framework (open source) and ABP Commercial [documents](https://docs.abp.io/) are completely separated. You can switch between them on the left side: + +![docs-project-selection](docs-project-selection.png) + +Based on our and customers' experiences, there are some problems with that approach: + +* Getting started, development tutorials, release notes, road map and some other documents are duplicated (or very similar) among ABP Framework and ABP Commercial documents. +* For ABP Commercial users, it is not clear if they also need to read the ABP Framework (open source) documentation or not. Also, when they read the framework document, some parts are different for ABP Commercial users, and it is also not clear in some cases. + +We are currently working to completely merge the ABP Framework (open source) and ABP Commercial documentation, remove duplications and revisit the contents. We will clearly indicate if a part of a document requires a paid license. + +The left navigation panel tree is also completely revisited and simplified: + +![docs-new-navigation](docs-new-navigation.png) + +## The ABP Studio Community Edition + +[ABP Studio](https://docs.abp.io/en/commercial/latest/studio/index) is a cross-platform desktop application designed for ABP and .NET developers. It aims to provide a comfortable development environment by automating tasks, providing insights about your solution, and simplifying the processes of creation, development, execution, browsing, monitoring, tracing, and deploying your solutions. + +Here, a screenshot from the *Solution Runner* screen of ABP Studio: + +![abp-studio-solution-runner](abp-studio-solution-runner.png) + +ABP Studio has been started as a commercial product, as a part of [ABP Commercial](https://commercial.abp.io/). We are very excited to announce that the *Community Edition* will be available soon for free. It will have some missing features and limitations compared to the full edition, but will be enough to create, explore and run ABP solutions easily. + +We will be offering ABP Studio as a starting point to the ABP platform. The [Getting Started](https://docs.abp.io/en/abp/latest/Getting-Started-Overall) and other documents will use ABP Studio to create new solutions and perform ABP-related operations. + +## Other News + +We are also working on some other topics related to these changes. Some of them are; + +* Completely renewing the [startup templates](https://docs.abp.io/en/abp/latest/Startup-Templates/Index) (with ABP Studio), so they will be more flexible and will provide more options. +* Providing a tool to automatically convert ABP solutions created with open source startup templates into ABP commercial. + +## Questions + +I tried to explain all the important changes in this post. However, you may have some questions in your mind. + +### What should open source users expect? + +Since the [ABP Commercial](https://commercial.abp.io/) website content is merged with the main [abp.io](https://abp.io/) website, you will see paid features being introduced on the main website. The pricing page will also be available on the same website. This may lead you to wonder whether the ABP Platform is a fully paid product. The simple answer to this question is "No". Actually, nothing has changed on the open source side. Everything will be the same. Additionally, open source users will now have ABP Studio Community Edition for free. So open source has more for its users than before. + +### What should ABP Commercial customers expect? + +ABP Commercial license holders may wonder if any license change happens. The answer is "No". All the license types, rules, restrictions and features are the same. With the changes explained in this post, you will follow the documentation easier (since you won't need to go to another website for the framework documentation) and you will better understand what special features are available to you. + +## Last Words + +With this post, we wanted to announce the changes to be made on the ABP platform to the ABP community, so don't be surprised or curious about what happened. If you have any questions or suggestions, feel free to write a comment for this blog post or send an email to info@abp.io. + diff --git a/docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/images/blob-storage.png b/docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/images/blob-storage.png new file mode 100644 index 0000000000..05aa32eaad Binary files /dev/null and b/docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/images/blob-storage.png differ diff --git a/docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/post.md b/docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/post.md new file mode 100644 index 0000000000..e76f7f2728 --- /dev/null +++ b/docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/post.md @@ -0,0 +1,114 @@ +# Using Blob Storage with ABP +ABP Framework provides a comprehensive solution to meet the needs of modern application development, while addressing the important requirement of BLOB Storing. ABP Framework [provides an easy integration for Blob Storing](https://docs.abp.io/en/abp/latest/Blob-Storing) and offers many storage services that you can easily integrate. In addition to efficiently storing large files, these services offer significant advantages such as scalability, security and backup. + +## What is Blob Storage ? + +Blob Storage is a service for storing unstructured data. It is becoming increasingly important to efficiently store and manage large data types (e.g. images, videos, documents). Blob Storage was developed to meet these needs and offers a secure solution with the advantages of scalability, durability and low cost. + + ![Blob Stroge](./images/blob-storage.png) + + +## How to use Blob Storage ? + +I would like to explain this to you with an example.For example, storing large files such as user profile pictures in the database negatively affects the performance and database.You can store this data using Blob storage. One of the advantages of storing user profile pictures in blob storage is that it improves database performance. Blob storage is a more efficient option than storing large size files in the database and allows database queries to run faster. Furthermore, blob storage provides scalability, so that the number of profile pictures can grow with the number of users, but without storage issues. This approach also maintains database normalization and makes the database design cleaner. + + How do we store user profile pictures with Blob Storage using ABP Framework? + +- #### Step 1: Configure the Blob Container + +Define a Blob Container named `profile-pictures` using the `[BlobContainerName("profile-pictures")]` attribute. + +````csharp +[BlobContainerName("profile-pictures")] +public class ProfilePictureContainer +{ + +} +```` +- #### Step 2: Create the ProfileAppService (Saving & Reading BLOBs) + +Create the `ProfileAppService` class and derive it from the `ApplicationService` class. This class will perform the necessary operations to store and retrieve profile pictures. + +````csharp +using Volo.Abp.Application.Services; + +public class ProfileAppService : ApplicationService +{ + // Code snippets will come here +} +```` + +- #### Step 3: Inject the `IBlobContainer` Service + +Inject the `IBlobContainer` service, in the constructor of the `ProfileAppService` class. The `IBlobContainer` is the main interface to store and read BLOB and is used to interact with the container. + +````csharp +private readonly IBlobContainer _blobContainer; + +public ProfileAppService(IBlobContainer blobContainer) +{ + _blobContainer = blobContainer; +} +```` + +- #### Step 4: Save Profile Picture + +The SaveProfilePictureAsync method is used to store the user's profile picture. A unique name is generated based on the user's credentials and the profile picture byte array with this name is saved in the Blob Container. + +````csharp +public async Task SaveProfilePictureAsync(byte[] bytes) +{ + var blobName = CurrentUser.GetId().ToString(); + await _blobContainer.SaveAsync(blobName, bytes); +} +```` + +- #### Step 5: Getting Profile Picture + +The GetProfilePictureAsync method is used to get the user's profile picture. A profile picture byte array is retrieved from the Blob Container with a specified name based on the user's credential. + +````csharp +public async Task GetProfilePictureAsync() +{ + var blobName = CurrentUser.GetId().ToString(); + return await _blobContainer.GetAllBytesOrNullAsync(blobName); +} +```` + + +Finally, add controls in the user interface that will allow users to upload and view their profile pictures. These controls will perform the operations by calling the corresponding methods in the ProfileAppService class. + +These steps cover the basic steps to store user profile pictures with Blob Storage using the ABP Framework. [Check out the documentation for more information.](https://docs.abp.io/en/abp/latest/Blob-Storing) + + +## What are the Advantages/Disadvantages of Keeping the BLOBs in a Database? + +#### Advantages: + +- Data Integrity and Relational Model: To ensure data integrity and preserve the relational model, it is important to store blob data in the database. This approach preserves the relationships between data and maintains the structural integrity of the database. + +- A Single Storage Location: Storing blob data in the database allows you to collect all data in a single storage location. This simplifies the management of data and increases data integrity. + +- Advanced Security Controls: Database systems often offer advanced security controls. Storing blob data in a database allows you to take advantage of these security features and ensures that data is accessed by authorized users. + +#### Disadvantages: + +- Performance Issues: Storing blob data in a database can negatively impact database performance. Oversized blob data can slow down query processing and reduce database performance. + +- Storage Space Issue: Storing blob data in the database can increase the size of the database and require more storage space. This can increase storage costs and complicate infrastructure requirements. + +- Backup and Recovery Challenges: Storing blob data in a database can make backup and recovery difficult. The large size of blob data can make backup and recovery time-consuming and data recovery difficult. + + +## Other Blob Storage Providers + +ABP Framework provides developers with a variety of options and flexibility by offering integration infrastructure for multiple cloud providers. This makes it easy for users to choose between different cloud platforms and select the most suitable solution for their business needs. + + +- Azure Blob Storage: A cloud storage service offered on the Microsoft Azure platform. It is used to store and access large amounts of data. It supports various data types such as files, images, videos and provides high scalability. ABP Framework provides integration with [Azure Blob Storage](https://docs.abp.io/en/abp/latest/Blob-Storing-Azure). + +- Aliyun Object Storage Service (OSS): OSS, Alibaba Cloud's cloud storage service, is an ideal solution for use cases such as big data storage, backup and media storage. It offers flexible storage options and provides a high level of security. ABP Framework interfaces with [Aliyun Blob Storage](https://docs.abp.io/en/abp/latest/Blob-Storing-Aliyun), making it easier for developers to manage data storage and access. + +- MinIO: MinIO is known as an open source object storage system and offers an Amazon S3 compatible cloud storage solution. It is a high-performance, scalable and fast storage service. ABP Framework integrates with [MinIO Blob Storage](https://docs.abp.io/en/abp/latest/Blob-Storing-Minio) to provide developers with cloud-based file and object storage. + +- Amazon Simple Storage Service (S3): Amazon S3 is a cloud storage service offered on the Amazon Web Services (AWS) platform. It can be used to store virtually unlimited amounts of data. It provides high durability, scalability and low cost.ABP Framework integrates with [Amazon S3 Blob Storage](https://docs.abp.io/en/abp/latest/Blob-Storing-Aws) to provide developers with cloud-based file and object storage. diff --git a/docs/en/Community-Articles/2024-05-01-How-ABP-get-current-user/POST.md b/docs/en/Community-Articles/2024-05-01-How-ABP-get-current-user/POST.md new file mode 100644 index 0000000000..5e32e184e1 --- /dev/null +++ b/docs/en/Community-Articles/2024-05-01-How-ABP-get-current-user/POST.md @@ -0,0 +1,201 @@ +# How claim type works in ASP NET Core and ABP Framework + +## The Claim Type + +A web application may use one or more authentication schemes to obtain the current user's information, such as `Cookies`, `JwtBearer`, `OpenID Connect`, `Google` etc. + +After authentication, we get a set of claims that can be issued using a trusted identity provider. A claim is a type/name-value pair representing the subject. The type property provides the semantic content of the claim, that is, it states what the claim is about. + +The [`ICurrentUser`](https://docs.abp.io/en/abp/latest/CurrentUser) service of the ABP Framework provides a convenient way to access the current user's information from the claims. + +The claim type is the key to getting the correct value of the current user, and we have a static `AbpClaimTypes` class that defines the names of the standard claims in the ABP Framework: + +```cs +public static class AbpClaimTypes +{ + public static string UserId { get; set; } = ClaimTypes.NameIdentifier; + public static string UserName { get; set; } = ClaimTypes.Name; + public static string Role { get; set; } = ClaimTypes.Role; + public static string Email { get; set; } = ClaimTypes.Email; + //... +} +``` + +As you can see, the default claim type of `AbpClaimTypes` comes from the [`System.Security.Claims.ClaimTypes`](https://learn.microsoft.com/en-us/dotnet/api/system.security.claims.claimtypes) class, which is the recommended practice in NET. + +## Claim type in different authentication schemes + +We usually see two types of claim types in our daily development. One of them is the [`System.Security.Claims.ClaimTypes`](https://learn.microsoft.com/en-us/dotnet/api/system.security.claims.claimtypes) and the other one is the `OpenId Connect` [standard claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims). + +### ASP NET Core Identity + +There is a [`ClaimsIdentityOptions`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.claimsidentityoptions) property in the `IdentityOptions`, which can be used to configure the claim type: + +| Property | Description | +|----------------------|---------------------------------------------------------------------------------------------------------------| +| EmailClaimType | Gets or sets the ClaimType used for the user email claim. Defaults to Email. | +| RoleClaimType | Gets or sets the ClaimType used for a Role claim. Defaults to Role. | +| SecurityStampClaimType | Gets or sets the ClaimType used for the security stamp claim. Defaults to "AspNet.Identity.SecurityStamp". | +| UserIdClaimType | Gets or sets the ClaimType used for the user identifier claim. Defaults to NameIdentifier. | +| UserNameClaimType | Gets or sets the ClaimType used for the user name claim. Defaults to Name. | + +* The Identity creates a `ClaimsIdentity` object with the claim type that you have configured in the `ClaimsIdentityOptions` class. +* The ABP Framework configures it based on `AbpClaimTypes,` so usually you don't need to worry about it. + +### JwtBearer/OpenID Connect Client + +The `JwtBearer/OpenID Connect` gets claims from `id_token` or fetches user information from the `AuthServer`, and then maps/adds it to the current `ClaimsIdentity`. + +To map the [standard claim](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) type to the [`System.Security.Claims.ClaimTypes`](https://learn.microsoft.com/en-us/dotnet/api/system.security.claims.claimtypes) via [azure-activedirectory-identitymodel-extensions-for-dotnet](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) library by default, which is maintained by the Microsoft team: + +```cs +Dictionary ClaimTypeMapping = new Dictionary +{ + { "actort", ClaimTypes.Actor }, + { "birthdate", ClaimTypes.DateOfBirth }, + { "email", ClaimTypes.Email }, + { "family_name", ClaimTypes.Surname }, + { "gender", ClaimTypes.Gender }, + { "given_name", ClaimTypes.GivenName }, + { "nameid", ClaimTypes.NameIdentifier }, + { "sub", ClaimTypes.NameIdentifier }, + { "website", ClaimTypes.Webpage }, + { "unique_name", ClaimTypes.Name }, + { "oid", "http://schemas.microsoft.com/identity/claims/objectidentifier" }, + { "scp", "http://schemas.microsoft.com/identity/claims/scope" }, + { "tid", "http://schemas.microsoft.com/identity/claims/tenantid" }, + { "acr", "http://schemas.microsoft.com/claims/authnclassreference" }, + { "adfs1email", "http://schemas.xmlsoap.org/claims/EmailAddress" }, + { "adfs1upn", "http://schemas.xmlsoap.org/claims/UPN" }, + { "amr", "http://schemas.microsoft.com/claims/authnmethodsreferences" }, + { "authmethod", ClaimTypes.AuthenticationMethod }, + { "certapppolicy", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/applicationpolicy" }, + { "certauthoritykeyidentifier", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/authoritykeyidentifier" }, + { "certbasicconstraints", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/basicconstraints" }, + { "certeku", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/eku" }, + { "certissuer", "http://schemas.microsoft.com/2012/12/certificatecontext/field/issuer" }, + { "certissuername", "http://schemas.microsoft.com/2012/12/certificatecontext/field/issuername" }, + { "certkeyusage", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/keyusage" }, + { "certnotafter", "http://schemas.microsoft.com/2012/12/certificatecontext/field/notafter" }, + { "certnotbefore", "http://schemas.microsoft.com/2012/12/certificatecontext/field/notbefore" }, + { "certpolicy", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatepolicy" }, + { "certpublickey", ClaimTypes.Rsa }, + { "certrawdata", "http://schemas.microsoft.com/2012/12/certificatecontext/field/rawdata" }, + { "certserialnumber", ClaimTypes.SerialNumber }, + { "certsignaturealgorithm", "http://schemas.microsoft.com/2012/12/certificatecontext/field/signaturealgorithm" }, + { "certsubject", "http://schemas.microsoft.com/2012/12/certificatecontext/field/subject" }, + { "certsubjectaltname", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/san" }, + { "certsubjectkeyidentifier", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/subjectkeyidentifier" }, + { "certsubjectname", "http://schemas.microsoft.com/2012/12/certificatecontext/field/subjectname" }, + { "certtemplateinformation", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatetemplateinformation" }, + { "certtemplatename", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatetemplatename" }, + { "certthumbprint", ClaimTypes.Thumbprint }, + { "certx509version", "http://schemas.microsoft.com/2012/12/certificatecontext/field/x509version" }, + { "clientapplication", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-application" }, + { "clientip", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-ip" }, + { "clientuseragent", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent" }, + { "commonname", "http://schemas.xmlsoap.org/claims/CommonName" }, + { "denyonlyprimarygroupsid", ClaimTypes.DenyOnlyPrimaryGroupSid }, + { "denyonlyprimarysid", ClaimTypes.DenyOnlyPrimarySid }, + { "denyonlysid", ClaimTypes.DenyOnlySid }, + { "devicedispname", "http://schemas.microsoft.com/2012/01/devicecontext/claims/displayname" }, + { "deviceid", "http://schemas.microsoft.com/2012/01/devicecontext/claims/identifier" }, + { "deviceismanaged", "http://schemas.microsoft.com/2012/01/devicecontext/claims/ismanaged" }, + { "deviceostype", "http://schemas.microsoft.com/2012/01/devicecontext/claims/ostype" }, + { "deviceosver", "http://schemas.microsoft.com/2012/01/devicecontext/claims/osversion" }, + { "deviceowner", "http://schemas.microsoft.com/2012/01/devicecontext/claims/userowner" }, + { "deviceregid", "http://schemas.microsoft.com/2012/01/devicecontext/claims/registrationid" }, + { "endpointpath", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path" }, + { "forwardedclientip", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-forwarded-client-ip" }, + { "group", "http://schemas.xmlsoap.org/claims/Group" }, + { "groupsid", ClaimTypes.GroupSid }, + { "idp", "http://schemas.microsoft.com/identity/claims/identityprovider" }, + { "insidecorporatenetwork", "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork" }, + { "isregistereduser", "http://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser" }, + { "ppid", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" }, + { "primarygroupsid", ClaimTypes.PrimaryGroupSid }, + { "primarysid", ClaimTypes.PrimarySid }, + { "proxy", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-proxy" }, + { "pwdchgurl", "http://schemas.microsoft.com/ws/2012/01/passwordchangeurl" }, + { "pwdexpdays", "http://schemas.microsoft.com/ws/2012/01/passwordexpirationdays" }, + { "pwdexptime", "http://schemas.microsoft.com/ws/2012/01/passwordexpirationtime" }, + { "relyingpartytrustid", "http://schemas.microsoft.com/2012/01/requestcontext/claims/relyingpartytrustid" }, + { "role", ClaimTypes.Role }, + { "roles", ClaimTypes.Role }, + { "upn", ClaimTypes.Upn }, + { "winaccountname", ClaimTypes.WindowsAccountName }, +}; +``` + +#### Disable JwtBearer/OpenID Connect Client Claim Type Mapping + +To turn off the claim type mapping, you can set the `MapInboundClaims` property of `JwtBearerOptions` or `OpenIdConnectOptions` to `false`. Then, you can get the original claim types from the token(`access_token` or `id_token`): + +JWT Example: + +```json +{ + "iss": "https://localhost:44305/", + "exp": 1714466127, + "iat": 1714466127, + "aud": "MyProjectName", + "scope": "MyProjectName offline_access", + "sub": "ed7f5cfd-7311-0402-245c-3a123ff787f9", + "unique_name": "admin", + "preferred_username": "admin", + "given_name": "admin", + "role": "admin", + "email": "admin@abp.io", + "email_verified": "False", + "phone_number_verified": "False", +} +``` + +### OAuth2(Google, Facebook, Twitter, Microsoft) Extenal Login Client + +The `OAuth2 handler` fetchs a JSON containing user information from the `OAuth2` server. The third-party provider issues the claim type based on their standard server and then maps/adds it to the current `ClaimsIdentity`. The ASP NET Core provides some built-in claim-type mappings for different providers as can be seen below examples: + +**Example**: The `ClaimActions` property of the `GoogleOptions` maps the Google's claim types to [`System.Security.Claims.ClaimTypes`](https://learn.microsoft.com/en-us/dotnet/api/system.security.claims.claimtypes): + +```cs +ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id"); // v2 +ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub"); // v3 +ClaimActions.MapJsonKey(ClaimTypes.Name, "name"); +ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name"); +ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name"); +ClaimActions.MapJsonKey("urn:google:profile", "link"); +ClaimActions.MapJsonKey(ClaimTypes.Email, "email"); +``` + +**Example**: The `ClaimActions` property of the `FacebookOptions` maps the Facebook's claim types to [`System.Security.Claims.ClaimTypes`](https://learn.microsoft.com/en-us/dotnet/api/system.security.claims.claimtypes): + +```cs +ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id"); +ClaimActions.MapJsonSubKey("urn:facebook:age_range_min", "age_range", "min"); +ClaimActions.MapJsonSubKey("urn:facebook:age_range_max", "age_range", "max"); +ClaimActions.MapJsonKey(ClaimTypes.DateOfBirth, "birthday"); +ClaimActions.MapJsonKey(ClaimTypes.Email, "email"); +ClaimActions.MapJsonKey(ClaimTypes.Name, "name"); +ClaimActions.MapJsonKey(ClaimTypes.GivenName, "first_name"); +ClaimActions.MapJsonKey("urn:facebook:middle_name", "middle_name"); +ClaimActions.MapJsonKey(ClaimTypes.Surname, "last_name"); +ClaimActions.MapJsonKey(ClaimTypes.Gender, "gender"); +ClaimActions.MapJsonKey("urn:facebook:link", "link"); +ClaimActions.MapJsonSubKey("urn:facebook:location", "location", "name"); +ClaimActions.MapJsonKey(ClaimTypes.Locality, "locale"); +ClaimActions.MapJsonKey("urn:facebook:timezone", "timezone"); +``` + +### OpenIddict AuthServer + +The `OpenIddict` uses the [standard claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) as the claim type of the `id_token` or `access_token` and `UserInfo` endpoint response, etc. + +* For JWT token, it also uses the [azure-activedirectory-identitymodel-extensions-for-dotnet](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) to get the claims from the `id_token` or `access_token`. +* For reference token, it gets the claims from the `database`. + +## Summary + +Once you find the claims you received do not meet your expectations, follow the instructions above to troubleshoot the problem. + +This article can help you understand the claim type in the ABP Framework and ASP NET Core. + diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/POST.md b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/POST.md new file mode 100644 index 0000000000..6d0c60407d --- /dev/null +++ b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/POST.md @@ -0,0 +1,85 @@ +# Deploy Your ABP Framework MVC Project to Azure Container Apps + +![](azure-container-abp.png) + +In this article, we will show the seamless deployment of an ABP Framework MVC project to Azure Container Apps. enabling you to deploy and run containerized applications without the hassle of managing the infrastructure underneath. It provides an uncomplicated and cost-effective method for deploying and scaling your applications. + +### Getting Started with ABP Framework MVC and Azure Container Apps + +To get started, you will need an ABP Framework MVC project that you want to deploy. If you don't have one, you can [create a new project using the ABP CLI](https://docs.abp.io/en/abp/latest/Startup-Templates/Application). You will also need [an Azure subscription](https://azure.microsoft.com) and [an Azure SQL database](https://azure.microsoft.com/en-gb/products/azure-sql). + +Before creating Azure container apps resources and deploying the ABP Framework MVC project, I show you how you can effortlessly create Docker images and push them to Docker Hub, leveraging the pre-configured Docker file and scripts that come with the ABP MVC framework. + +### Creating a Docker Image for ABP Framework MVC + +To create a Docker image for your ABP Framework MVC project, navigate to `etc/build/build-images-locally.ps1` and fix the script to match your Docker Hub username and image name. Then, run the script to build the Docker image locally. + +![Build Docker Image](build-docker-image.png) + +Next, check the Docker Hub repository to confirm that the image has been pushed successfully. + +![Docker Hub Repository](docker-hub-repository.png) + +### Deploying to Azure Container Apps + +Now that you have Docker images for your ABP Framework MVC project, you can proceed to deploy it to Azure Container Apps. To do this, navigate to the Azure portal and create a new Azure Container Apps resource. Ypu will not need just an Azure Container Apps resource, but also an Azure Container Apps Job resource to migrate the database schema and seed data for your ABP Framework MVC project. + +![Create Azure Container Apps](create-azure-container-apps.png) + +#### Step 1: Deploy the Docker Image + +Firstly, create a new Azure Container Apps resource without environment variables. You will need web url so that you can set it as an environment variable in the next step. Then, check the deployment status to confirm that the deployment was successful. + +![Deploy Docker Image](deploy-docker-image.png) + +#### Step 2: Migrate Database Schema and Seed Data + +Secondly, create a new Azure Container Apps Job resource to migrate the database schema and seed data. You can do this by creating a new job with the following environment variables: + +```text +ConnectionStrings__Default - Server=tcp:demoabpapp.database.windows.net,1433;Initial Catalog=mvcapppro;Persist Security Info=False;User ID=demoapppro;Password={your_password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30; + +OpenIddict__Applications__mvcapppro_Web__RootUrl - https://mvcwebapp.victoriousgrass-8b06438d.northeurope.azurecontainerapps.io + +To get ConnectionStrings of Sql Database and url of the web app, you can navigate to the Azure portal and check the properties of the Azure SQL database and Azure Container Apps resource. +``` + +![Azure SQL Database Connection Strings](azure-sql-database-connection-strings.png) + +![Create Azure Container Apps Job](create-azure-container-apps-job.png) + +Finally, check the job status to confirm that the database migration and seeding were successful. You can connect to the Azure SQL database to verify that the schema and seed data have been applied. + +![Check Job Status](check-job-status.png) + +#### Step 3: Edit the Azure Container Apps Resource + +After completing these steps, you have to edit the Azure Container Apps resource to add the required environment variables for your ABP Framework MVC project. You can do this by adding the following environment variables: + +```text +App__SelfUrl - https://mvcwebapp.victoriousgrass-8b06438d.northeurope.azurecontainerapps.io + +ASPNETCORE_URLS - http://+:80 + +AuthServer__Authority - https://mvcwebapp.victoriousgrass-8b06438d.northeurope.azurecontainerapps.io + +ConnectionStrings__Default - Server=tcp:demoabpapp.database.windows.net,1433;Initial Catalog=mvcapppro;Persist Security Info=False;User ID=demoapppro;Password={your_password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30; +``` + +![Add Environment Variables](add-environment-variables.png) + +#### Step 4: Create a New Deployment + +Once you have added the environment variables, save and create a new deployment to apply the changes. You can now access your ABP Framework MVC project running on Azure Container Apps by navigating to the URL provided in the environment variables. + +![Access ABP Framework MVC Project](access-abp-framework-mvc-project.png) + +You can see the Azure resources created for the ABP Framework MVC project deployment that includes the Azure Container Apps resource, Azure Container Apps Job resource, and Azure SQL database. + +![Azure Resources](azure-resources.png) + +### Conclusion + +Azure Container Apps provides a simple and cost-effective way to deploy and scale your ABP Framework MVC projects without managing the underlying infrastructure. By following the steps outlined in this article, you can seamlessly deploy your ABP Framework MVC projects to Azure Container Apps and enjoy the benefits it offers. + +I hope you found this article helpful. If you have any questions or feedback, please feel free to leave a comment below. diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/access-abp-framework-mvc-project.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/access-abp-framework-mvc-project.png new file mode 100644 index 0000000000..ff0aa2f811 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/access-abp-framework-mvc-project.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/add-environment-variables.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/add-environment-variables.png new file mode 100644 index 0000000000..56f9df7fa4 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/add-environment-variables.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-container-abp.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-container-abp.png new file mode 100644 index 0000000000..ec96dd0b2e Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-container-abp.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-resources.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-resources.png new file mode 100644 index 0000000000..3bab322580 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-resources.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-sql-database-connection-strings.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-sql-database-connection-strings.png new file mode 100644 index 0000000000..5ec51f6edb Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-sql-database-connection-strings.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/build-docker-image.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/build-docker-image.png new file mode 100644 index 0000000000..496dd637cd Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/build-docker-image.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/check-job-status.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/check-job-status.png new file mode 100644 index 0000000000..e87ee2081f Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/check-job-status.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps-job.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps-job.png new file mode 100644 index 0000000000..10229d5707 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps-job.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps.png new file mode 100644 index 0000000000..3e8e899960 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/deploy-docker-image.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/deploy-docker-image.png new file mode 100644 index 0000000000..df431b6c51 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/deploy-docker-image.png differ diff --git a/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/docker-hub-repository.png b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/docker-hub-repository.png new file mode 100644 index 0000000000..b25e01a629 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/docker-hub-repository.png differ diff --git a/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/adding-incorrect-data.png b/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/adding-incorrect-data.png new file mode 100644 index 0000000000..6c8a704093 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/adding-incorrect-data.png differ diff --git a/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/error.png b/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/error.png new file mode 100644 index 0000000000..7c611cdc49 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/error.png differ diff --git a/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/validation.png b/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/validation.png new file mode 100644 index 0000000000..6131739d68 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/validation.png differ diff --git a/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/post.md b/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/post.md new file mode 100644 index 0000000000..494f6a9a0a --- /dev/null +++ b/docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/post.md @@ -0,0 +1,115 @@ +# Using FluentValidation with ABP Framework + +## What is Validation? Why Do We Need to Validate? +Validation is checking whether data is valid or not. We can liken validations to the cell membrane of our application. Just as the cell does not want to let in harmful substances, we do not want to add erroneous data to the database, which is critical to our application. Validations allow data that follows the rules to reach the database. Data that does not comply with the rules will not access the database at all, and the operation will fail. + +![Validation](./images/validation.png) + +Validations make your application run more efficiently as they are not directly tied to the database. They should be implemented across various layers of the application, including the UI, backend, and database, to prevent malicious users from bypassing these checks. Fluent validation is frequently employed for creating backend validations. + +## What is Fluent Validation? +Fluent validation is a library for checking whether data is valid or not. Fluent validation can be applied to your code in a fluent and understandable way. + +## Why We Should Use Fluent Validation? +Fluent Validation allows you to define your validation rules in a clear and flexible way. This means you can comfortably handle complex validation scenarios in your code. This makes your development process much more manageable. The readability that Fluent Validation offers really makes things easier. Having a clear understanding of what your validation rules do is a huge advantage when working on your code. In short, your code is cleaner and clearer. Fluent Validation is also very functional in terms of testability. By defining your validation rules in separate classes, you can easily test and maintain these rules. Fluent Validation is a widely used validation library on the .NET platform. As such, it has become a common standard among .NET developers. This provides advantages in terms of community support and compatibility. So, using Fluent Validation simplifies your development process by making your code more understandable, manageable and testable. + +In this section, I will show you how to use FluentValidation library within an ABP-based application. Therefore, I assume that you have an ABP-based application that has already been created. If you haven't created an ABP-based application yet, please follow the [Getting Started documentation](https://docs.abp.io/en/abp/latest/Getting-Started-Create-Solution?UI=MVC&DB=EF&Tiered=No). + +Using Fluent validation with Abp is quite simple. Open a command line window in the folder of the project (.csproj file) and type the following command: + +```bash +abp add-package Volo.Abp.FluentValidation +``` + +If you have not created your abp project, review the [steps to create](https://docs.abp.io/en/abp/latest/Tutorials/Todo/Overall) it now. + +Create the `Product` entity as below: + +````csharp + public class Product: FullAuditedAggregateRoot + { + public string Name { get; set; } = string.Empty; + public decimal Price { get; set; } + public int Stock { get; set; } + public string? LicenseKey { get; set; } + } +```` + +You must have ProductCreateDto : + +````csharp + public class ProductCreateDto + { + public string Name { get; set; } + public decimal Price { get; set; } + public int Stock { get; set; } + public string? LicenseKey { get; set; } + } +```` +Create the `ProductCreateDtoValidator` class in the **Products** folder in the `Application.Contracts` project: + +````csharp + public class ProductCreateDtoValidator :AbstractValidator +{ + public ProductCreateDtoValidator() + { + RuleFor(p=>p.Name). + NotEmpty(). + WithMessage("Product name cannot be empty"); + + RuleFor(p=>p.Name). + MinimumLength(3). + MaximumLength(100). + WithMessage("Product name must be between 3 and 100 characters"); + + RuleFor(p => p.Stock). + GreaterThanOrEqualTo(0). + WithMessage("Product stock should not be negative"); + + RuleFor(p => p.Price). + GreaterThanOrEqualTo(0). + WithMessage("Product Price should not be negative"); + } +} +```` +The validator class you create should inherit from `AbstractValidator`. You should give `AbstractValidator` the class you want to validate generically. You have to create a constructor method. You must write the code in this constructor method. Fluent validation provides you with ready-made methods that you can use to write validations very easily. + +The **RuleFor** method allows you to write a validation rule. You must specify in the parameter for which property you want to write a validation rule. + +With the **NotEmpty** method you can specify that a property cannot be null. + +With **MinimumLength** and **MaximumLength** you can specify the minimum and maximum number of characters the property can take. + +With the **GreaterThanOrEqualTo** method you can specify that the value of the property must be greater than or equal to the value entered as a parameter. + +**WithMessage** method you can specify the message to be sent when the validation fails. + +You can add a method to write your own customized validation code. For example, let's write the code that requires the license key field to contain the word “key”. + +````csharp + private bool ContainsKey(string arg) + { + return arg.IndexOf("key", StringComparison.OrdinalIgnoreCase) >= 0; + } +```` + +Add the code to the constructor to use this method: + +````csharp +RuleFor(p => p.LicenseKey). + Must(ContainsKey). + WithMessage("Product license key must contain the word 'key'"); + +```` +Try to add data that does not meet the validation rules + +![Adding Incorrect Data](./images/adding-incorrect-data.png) + +If one of the validation rules does not meet the rules, then the following error will be received for the custom rule that we defined: + +![Error](./images/error.png) + +For more information on fluent validation with abp framework, see the [documentation](https://docs.abp.io/en/abp/latest/FluentValidation) + +For more information on fluent validation, see the [documentation](https://docs.fluentvalidation.net/en/latest/)  + diff --git a/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/automapper.png b/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/automapper.png new file mode 100644 index 0000000000..103e8f2261 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/automapper.png differ diff --git a/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger1.png b/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger1.png new file mode 100644 index 0000000000..a9f245de18 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger1.png differ diff --git a/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger2.png b/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger2.png new file mode 100644 index 0000000000..8bfcac1609 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger2.png differ diff --git a/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/post.md b/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/post.md new file mode 100644 index 0000000000..283dd8e00d --- /dev/null +++ b/docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/post.md @@ -0,0 +1,134 @@ +# What is Object to Object Mapping? + +Mapping the properties of one object to the properties of another object is called **Object to Object Mapping**. Most of the time, you don't want to show the data you store in your database to end users as it is. Instead, you only return users the information they need for that operation and reduce the output size. + +For example, in database tables that contain relationships, we analyze the relationships and present meaningful data to users. Suppose we have a product and a category object, we keep a property called `categoryId` in the `Product` entity. However, it would be illogical to show the `categoryId ` property to users. Therefore, we can create a DTO (data transfer object) and show the **category name** to the end users, instead of the `categoryId` directly. + +DTOs are used to transfer data of objects from one object to another one. We often need to map our entities to DTOs and DTOs to entities. For example, consider the code below: + +````csharp + public virtual async Task CreateAsync(CustomerCreateDto input) + { + + var customer = await _customerManager.CreateAsync( + input.BirthDay, input.MembershipDate, input.FirstName, input.LastName + ); + CustomerDto customerDto = new CustomerDto + { + Id = customer.Id, + FirstName = input.FirstName, + LastName = input.LastName, + // ...other + }; + + return customerDto; + } + +```` +As can be seen here, it's repetitive and tedious to manually map an object to another similar object. Also, it violates the [DRY principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself), makes your code more complicated and reduces readability. Instead of manually mapping objects, you can use the [AutoMapper](https://automapper.org/) library to automatically map two objects' properties: + + +````csharp + public virtual async Task CreateAsync(CustomerCreateDto input) + { + + var customer = await _customerManager.CreateAsync( + input.BirthDay, input.MembershipDate, input.FirstName, input.LastName + ); + + return ObjectMapper.Map(customer); + } +```` +The **ObjectMapper.Map** method allows you to convert your `Customer` entity to `CustomerDto`. `IObjectMapper` interface is a service, that comes from the **AutoMapper** library, so let's learn more about **AutoMapper** in the next section. + +# What is AutoMapper? + +Automapper is a .NET library that automates object-to-object mapping. ABP provides abstractions for object-to-object mapping and has an integration package to use [AutoMapper](http://automapper.org/) as the object mapper. + +Automapper is a library that transforms similar objects into each other. We can imagine Automapper as a machine that transforms an apple with a hat into an apple without a hat: + +![AutoMapper](./images/automapper.png) + +In this chapter, I will show you how to use the AutoMapper library in an ABP-based application. For this reason, I assume that you already have an ABP-based application created. If you have not yet created an ABP-based application, please follow the [Getting Started documentation](https://docs.abp.io/en/abp/latest/Getting-Started-Create-Solution?UI=MVC&DB=EF&Tiered=No). + +Create a domain entity similar to this one: + +````csharp + public class Customer : FullAuditedAggregateRoot + { + public string? FirstName { get; set; } + public string? LastName { get; set; } + public DateTime BirthDay { get; set; } + public DateTime MembershipDate { get; set; } + } +```` +`Customer` entity contains some properties (such as `FirstName`, `LastName`, ... and other audited properties coming from the base class - `DeleterId`, `IsDeleted`, `CreationTime` etc. -). Typically, you would not want to show/return all of these properties to end users, at that point, you can create a DTO class and only define the properties that you want to return to the end users. + +Let's create the `CustomerGetDto` class in the `*.Application.Contracts` project as follows: + +````csharp + public class CustomerGetDto + { + public string? FirstName { get; set; } + public string? LastName { get; set; } + public DateTime BirthDay { get; set; } + } +```` + +After creating our entity and output DTO classes, now in the application service implementation, we can return the `CustomerGetDto` class, as a result of listing the customers. For that reason, we can write a code as follows: + +````csharp +public virtual async Task> GetListAsync(GetCustomersInput input) + { + var totalCount = await _customerRepository.GetCountAsync(input.FilterText, input.FirstName, input.LastName, input.BirthDayMin, input.BirthDayMax, input.MembershipDateMin, input.MembershipDateMax); + var items = await _customerRepository.GetListAsync(input.FilterText, input.FirstName, input.LastName, input.BirthDayMin, input.BirthDayMax, input.MembershipDateMin, input.MembershipDateMax, input.Sorting, input.MaxResultCount, input.SkipCount); + + return new PagedResultDto + { + TotalCount = totalCount, + Items = ObjectMapper.Map, List>(items) + }; + } +```` +In this code, we first get the total number of our customers and all customers according to the specified filters, then we map `List` to `List` using the `ObjectMapper.Map` method from the **ApplicationService** base class. This way we have full control over which properties are returned to the end users. + +After mapping the `Customer` entity to the `CustomerGetDto` class. Before running our application, we should define the mappings in the `*AutoMapperProfile` class in the `*.Application` project as follows: + +````csharp +public class YourApplicationAutoMapperProfile : Profile +{ + public YourApplicationAutoMapperProfile() + { + CreateMap(); + } +} +```` +Finally, we can run our application and navigate to the **/swagger** endpoint to try our endpoint. When we send a request, we should get the result as follows: + +![Swagger](./images/swagger1.png) + +## Advanced: Mapping Configurations + +In some scenarios, you may want to make some customizations when mapping two objects. For example, let's assume that you want to create the `CustomerGetDto` class as follows: + +````csharp + public class CustomerGetDto + { + public string? FullName { get; set; } + public int Age { get; set; } + } +```` +AutoMapper can't map these properties automatically, because they do not exist in the source object, which is the `Customer` entity. Therefore, you need to specify the exception and update your `YourApplicationAutoMapperProfile` class as follows: + +````csharp + +CreateMap().ForMember(c=>c.FullName,opt=> opt.MapFrom(src => src.FirstName + " " + src.LastName)) + .ForMember(c=>c.Age, opt=> opt.MapFrom(src=> DateTime.UtcNow.Year -src.BirthDay.Year)); + +```` +This configuration concatenates and matches **FirstName** and **LastName** properties into the **FullName** property and subtracts **BirthDate** from today's year and assigns it to the customer's **Age**. +After these configurations, if you make a request to the relevant endpoint, the output will look like: + +![Swagger](./images/swagger2.png) + +For more information on object-to-object mapping with [ABP Framework](https://abp.io/), see the [documentation](https://docs.abp.io/en/abp/latest/Object-To-Object-Mapping). diff --git a/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/POST.md b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/POST.md new file mode 100644 index 0000000000..e6417b70ce --- /dev/null +++ b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/POST.md @@ -0,0 +1,340 @@ +# Sentiment Analysis Within ABP-Based Application + +In this article, first I will briefly explain what sentiment analysis is and then show you how you can apply sentiment analysis in an ABP-Based application (or any kind of .NET application). + +We will use ML.NET Framework, which is an open-source machine learning framework created by the dotnet team and also we will create a layered ABP Solution by using the application template and finally we will use CMS Kit's Comment Feature and extend its behavior by adding spam detection while creating or updating a comment, at that point we will make sentiment analysis. + +## Sentiment Analysis + +[Sentiment Analysis (or opinion mining)](https://en.wikipedia.org/wiki/Sentiment_analysis) refers to determining the emotion from the given input. The primary goal of sentiment analysis is to identify, extract, and categorize (positive, negative, or neutral) the sentiments expressed in textual data. + +To understand it better, let's check the following figure and examine the comments: + +![](sentiment-analysis.png) + +* If you look at these comments, you will notice that comments have ratings and it's easy to understand the emotion or thoughts of the users who commented about the related product. +* But even if there was not any rating specified for the given comments we still can get the emotion of the users. Because, as you can see, the comments specified some obvious words that express emotions, for example, in the first comment, the user says **he/she liked the product**, **it's easy to use** and **its battery is good**, and therefore this is obviously a positive comment. +* On the other hand, if we look at the second comment, we will notice some negative statements such as **useless phone**, **cannot maintain any data connection** and the user suggests **do not buy this phone**. Actually, this is what sentiment analysis is all about, abstracting the emotion from a given input, it's comment in that case but it can be any kind of input or input-group. + +## Demo: Spam Detection (Applying Sentiment Analysis) + +> You can get the source code of the demo from [https://github.com/EngincanV/SentimentAnalysisDemo](https://github.com/EngincanV/SentimentAnalysisDemo). + +In this demo application, we will create an [ABP-based application](https://docs.abp.io/en/abp/8.1/Startup-Templates/Application) and integrate the [ABP's CMS Kit Module's Comment Feature](https://docs.abp.io/en/abp/latest/Modules/Cms-Kit/Comments), which provides a comment system to add a comment to any kind of resource, such as blog posts or products. + +By default, CMS Kit's Comment Feature does not provide spam detection and therefore in this sample application, we will add [spam detection](https://github.com/EngincanV/SentimentAnalysisDemo/blob/master/src/SentimentAnalysisDemo.Application/ML/SpamDetector.cs) while creating or updating a comment. Thus, whenever a comment is being added or updated, the spam detection service will validate the comment and reject it if it contains spam content otherwise it will make the other validations and save the comment: + +![](sentiment-analysis-steps.png) + +To get started, we will first create an application, and add the CMS Kit Module to the solution and then we will enable the Comment Feature of the CMS Kit Module, and finally, we will add the Comment Component to the homepage and add spam detection by extending the behavior. Let's start with creating the application! + +### Creating an ABP-Based Application + +You can use the following command to create a layered ABP solution (with MongoDB as the database option): + +```bash +abp new SentimentAnalysisDemo -t app -d mongodb --version 8.1.1 +``` + +### Installing the CMS Kit Module + +After creating the project, we can add the CMS Kit module to our project. [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides the `add-module` command to install a specific module to a solution. + +You can use the following command to install the CMS Kit module into your application (run this command in the solution directory): + +```bash +abp add-module Volo.CmsKit --skip-db-migrations +``` + +After this command is executed, all related CMS Kit packages will be added to the correct layers and then you can enable any CMS Kit feature you want. + +### Enabling the Comment Feature + +By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can either enable all of them or enable them one by one. In our demo application, we only need the **Comment Feature**, therefore we can only enable it. + +To enable the Comment Feature, you can open the `SentimentAnalysisDemoGlobalFeatureConfigurator` class (under the `*.Domain.Shared` project) and update it as follows: + +```csharp +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Threading; + +namespace SentimentAnalysisDemo; + +public static class SentimentAnalysisDemoGlobalFeatureConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => + { + cmsKit.Comments.Enable(); + }); + }); + } +} +``` + +After enabling the feature, now we can make the final configurations and directly use it in our application. + +### Configurations for Comment Feature + +Open the `SentimentAnalysisDemoDomainModule` class and add the following code-block into the `ConfigureServices` method: + +```csharp + Configure(options => + { + options.EntityTypes.Add(new CommentEntityTypeDefinition("Comment")); + options.IsRecaptchaEnabled = true; + }); +``` + +Here, we simply defining what should be the entity-type name of our comment and also enable the reCaptcha for the comment system. After this configuration, now we can open the `Index.cshtml` file in the `*.Web` project and invoke the `CommentingViewComponent` as below: + +```html +@page +@using Microsoft.AspNetCore.Mvc.Localization +@using SentimentAnalysisDemo.Localization +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting +@model SentimentAnalysisDemo.Web.Pages.IndexModel + +
+
Comments:
+ + @await Component.InvokeAsync(typeof(CommentingViewComponent), new + { + entityType = "Comment", + entityId = "SentimentAnalysisDemo", + isReadOnly = false + }) +
+ +``` + +After adding the related component, now you can run the web project and see the comment component if you want. + +### Applying Sentiment Analysis (Creating the Spam Detection Service) + +By default, CMS Kit's Comment Feature does not provide a spam detection system. In this demo application, we will override the `CommentPublicAppService`'s `CreateAsync` and `UpdateAsync` methods and then will add the spam detection control whenever a new comment has been submitted or an existing one is being updated. + +To override the `CommentPublicAppService` and extend its use-case implementations, create a `MyCommentAppService` class and update its content as below: + +```csharp +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using SentimentAnalysisDemo.ML; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.CmsKit.Comments; +using Volo.CmsKit.Public.Comments; +using Volo.CmsKit.Users; + +namespace SentimentAnalysisDemo.Volo.CmsKit.Public.Comments; + +[Dependency(ReplaceServices = true)] +[ExposeServices(typeof(ICommentPublicAppService), typeof(CommentPublicAppService), typeof(MyCommentAppService))] +public class MyCommentAppService : CommentPublicAppService +{ + protected ISpamDetector SpamDetector { get; } + + public MyCommentAppService( + ICommentRepository commentRepository, + ICmsUserLookupService cmsUserLookupService, + IDistributedEventBus distributedEventBus, + CommentManager commentManager, + IOptionsSnapshot cmsCommentOptions, + ISpamDetector spamDetector + ) + : base(commentRepository, cmsUserLookupService, distributedEventBus, commentManager, cmsCommentOptions) + { + SpamDetector = spamDetector; + } + + public override async Task CreateAsync(string entityType, string entityId, CreateCommentInput input) + { + //Check message: spam or ham. + await SpamDetector.CheckAsync(input.Text); + + return await base.CreateAsync(entityType, entityId, input); + } + + public override async Task UpdateAsync(Guid id, UpdateCommentInput input) + { + //Check message: spam or ham. + await SpamDetector.CheckAsync(input.Text); + + return await base.UpdateAsync(id, input); + } +} +``` + +Here, we simply just inject the `ISpamDetector` service, which we will create in a minute, and use its `CheckAsync` method to make a spam check before the comment is created or updated. + +Now, we can create the `ISpamDetector` service in the `*.Application.Contracts` project as follows: + +```csharp +using System.Threading.Tasks; + +namespace SentimentAnalysisDemo.ML; + +public interface ISpamDetector +{ + Task CheckAsync(string text); +} +``` + +Then, we can create the `SpamDetector` and implement the `ISpamDetector` interface (in the `*.Application` project): + +```csharp +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.ML; +using SentimentAnalysisDemo.ML.Model; +using Volo.Abp; +using Volo.Abp.DependencyInjection; + +namespace SentimentAnalysisDemo.ML; + +public class SpamDetector : ISpamDetector, ITransientDependency +{ + public async Task CheckAsync(string text) + { + //check if the text contains a spam content or not... + + } +} +``` + +The `CheckAsync` method is where we need to make the sentiment analysis and detect if the comment contains spam content or not. If it's spam, then we should throw a [UserFriendlyException](https://docs.abp.io/en/abp/latest/Exception-Handling#user-friendly-exception) and notify the user that the comment should be updated and should not contain any spam content. + +#### Spam Detection + +Before, making the spam check, we should have a dataset to train a machine-learning model and add `Microsoft.ML` package into our project. For that purpose, I searched in [Kaggle](https://www.kaggle.com/) for spam datasets, found the **Spam-Mail-Detection-Dataset** from Kaggle, and downloaded the csv file to use in my application. Therefore, [you should also download the dataset from the link and put it under the **/ML/Data/spam_data.csv** directory of the `*.Web` project](https://github.com/EngincanV/SentimentAnalysisDemo/blob/master/src/SentimentAnalysisDemo.Web/ML/Data/spam_data.csv). + +Here is what our dataset looks like (**0 -> not spam / 1 -> spam**): + +| Category | Message | +|----------|---------| +| 0 | Is that seriously how you spell his name? | +| 1 | Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 87121 to receive entry question(std txt rate)T&C's apply 08452810075over18's | +| . | . | +| . | . | +| . | . | + +> **Note:** This dataset is not ready-to use in a real-world solution. It's for mail spam detection but for the simplicity of the sample, it's not important and can be used for development purposes. + +After, downloading the dataset and putting it in the directory of **/ML/Data**, now we can add the `Microsoft.ML` package into our `*.Application` project: + +```bash +dotnet add package Microsoft.ML +``` + +Finally, we can implement the `CheckAsync` method and use sentiment analysis to make spam checks as follows: + +```csharp + + public async Task CheckAsync(string text) + { + var dataPath = Path.Combine(Environment.CurrentDirectory, "ML", "Data", "spam_data.csv"); + + var mlContext = new MLContext(); + + //Step 1: Load Data 👇 + IDataView dataView = mlContext.Data.LoadFromTextFile(dataPath, hasHeader: true, separatorChar: ','); + + //Step 2: Split data to train-test data 👇 + DataOperationsCatalog.TrainTestData trainTestSplit = mlContext.Data.TrainTestSplit(dataView, testFraction: 0.2); + IDataView trainingData = trainTestSplit.TrainSet; //80% of the data. + IDataView testData = trainTestSplit.TestSet; //20% of the data. + + //Step 3: Common data process configuration with pipeline data transformations + choose and set the training algorithm 👇 + var estimator = mlContext.Transforms.Text.FeaturizeText(outputColumnName: "Features", inputColumnName: nameof(SentimentAnalyzeInput.Message)) + .Append(mlContext.BinaryClassification.Trainers.SdcaLogisticRegression(labelColumnName: "Label", featureColumnName: "Features")); + + //Step 4: Train the model 👇 + ITransformer model = estimator.Fit(trainingData); + + //Step 5: Predict 👇 + var sentimentAnalyzeInput = new SentimentAnalyzeInput + { + Message = text + }; + + var predictionEngine = mlContext.Model.CreatePredictionEngine(model); + var result = predictionEngine.Predict(sentimentAnalyzeInput); + if (IsSpam(result)) + { + throw new UserFriendlyException("Spam detected! Please update the message!"); + } + } + + private static bool IsSpam(SentimentAnalyzeResult result) + { + //1 -> spam / 0 -> ham (for 'Prediction' column) + return result is { Prediction: true, Probability: >= 0.5f }; + } + +``` + +Here, we have done the following things: + +1. **First, we loaded the data**: For that reason, we created a `MLContext` object, which is a main class for all ML.NET operations. Then, we used its `LoadFromTextFile` method and specified the dataset path in our application. Also, we mapped the dataset columns to the `SentimentAnalyzeInput` class, which we will create later on. +2. **For the second step, we split the data as training and testing data**: To be able to train a machine learning model and then evaluate its accuracy, we should not use all the data for training purposes, instead, we should split the data as training and testing data and after training the model, compare the training data accuracy with the testing data. +3. **For the third step, we should make data transformation, convert the text-based data into numeric vectors and then choose a training algorithm**: After splitting the data for training and testing purposes, now we can apply some data transformations for the *Message* column in our dataset. Because, as you would see, messages are text-based inputs and machine-learning algorithms work best with the numeric vectors. So, we are making data transformations and representing the data as numeric values. Then, we can apply `BinaryClassification` with the **SdcaLogicticRegression** algorithm to our training data. +4. **Train the model**: Since we make the data transformations and chose the correct algorithm for our model, now we can train the model. +5. **Predict the sample data**: Finally, we can pass a comment to this method and make spam check and either approve our reject the comment according to the predicted result. (To make predictions, we need to create a **PredictionEngine** and get the final results in the output class that we specified, `SentimentAnalyzeResult` in our example) + +Let's create the `SentimentAnalyzeInput` and `SentimentAnalyzeResult` classes as follows. + +**SentimentAnalyzeInput.cs:** + +```csharp +using Microsoft.ML.Data; + +namespace SentimentAnalysisDemo.ML.Model; + +public class SentimentAnalyzeInput +{ + [LoadColumn(0), ColumnName("Label")] + public bool Category { get; set; } + + [LoadColumn(1), ColumnName("Message")] + public string Message { get; set; } +} +``` + +**SentimentAnalyzeResult.cs:** + +```csharp +using Microsoft.ML.Data; + +namespace SentimentAnalysisDemo.ML.Model; + +public class SentimentAnalyzeResult +{ + [ColumnName("PredictedLabel")] + public bool Prediction { get; set; } + + public float Probability { get; set; } + + public float Score { get; set; } +} +``` + +Then, finally, we can run the application to see the final results: + +![](demo.gif) + +## Advanced: Reusing And Optimizing Machine Learning Models + +Once the model is trained and evaluated, we can save the trained model and use it directly for further use. In this way, you don’t have to retrain the model every time when you want to make predictions. It’s essential to save the trained model for future use and a must for the production-ready code. I created a separate article dedicated to that topic, and if you are interested, you can read it from [here](https://engincanv.github.io/machine-learning/sentiment-analysis/best-practises/2024/05/16/reusing-and-optimizing-machine-learning-models-in-dotnet.html). + +## Conclusion + +In this article, I briefly explain what sentiment analysis is, created a sample ABP-based application, integrated the CMS Kit Module and finally, applied sentiment analysis to make spam checks whenever a new comment has been submitted or updated. You can get the source code of the demo from [https://github.com/EngincanV/SentimentAnalysisDemo](https://github.com/EngincanV/SentimentAnalysisDemo) + +Thanks for reading :) diff --git a/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/cover-image.png b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/cover-image.png new file mode 100644 index 0000000000..7091f1a6b5 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/cover-image.png differ diff --git a/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/demo.gif b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/demo.gif new file mode 100644 index 0000000000..5a373ba110 Binary files /dev/null and b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/demo.gif differ diff --git a/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis-steps.png b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis-steps.png new file mode 100644 index 0000000000..4222b4c97c Binary files /dev/null and b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis-steps.png differ diff --git a/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis.png b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis.png new file mode 100644 index 0000000000..9597476dca Binary files /dev/null and b/docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis.png differ diff --git a/docs/en/UI/Angular/Title-Strategy-Service.md b/docs/en/UI/Angular/Title-Strategy-Service.md new file mode 100644 index 0000000000..64e94b3e17 --- /dev/null +++ b/docs/en/UI/Angular/Title-Strategy-Service.md @@ -0,0 +1,59 @@ +# Title Strategy For Angular + +## **ABP has a default title strategy for Angular UI**. + +This strategy is based on the title property. Provide a title property when setting a new route. + +**Example** + +```ts +{ + path: 'customers', + component: CustomersComponent, + title: 'AbpCustomers::Roles' +}, +``` + +- It is better to use localized text in the title property. It will be translated by **LocalizationService**. +- The **`title`** property is already set in **ABP internal packages**. + +## How it looks + +When you create a new route and provide a **`title`** property, it will look like this **` | <projectName>`** + +### What is `projectName` and How to Customize + +- **`projectName`** is the name of your application. By default, ABP sets a [**`projectName`**](https://github.com/abpframework/abp/blob/f48f78618a326644843c01424b093f0d79448769/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json#L4) for your application. This localization text is added for customization and can be changed for different languages. + +### Disable `projectName` + +- If you don't want to show **`projectName`** in the title, you can disable it. + +**app.module.ts** + +```ts +import { DISABLE_PROJECT_NAME } from '@abp/ng.core'; + +providers: [ + ..., + { provide: DISABLE_PROJECT_NAME, useValue: true} +], +``` + +- Now only title will be shown. + +## Override ABP's Default Title Strategy + +**app.module.ts** + +```ts +import { TitleStrategy } from '@angular/router'; +import { YourCustomStrategy } from './title-strategy.service.ts'; + +providers: [ + ..., + { provide: TitleStrategy, useExisting: YourCustomStrategy }, +], +``` + +- You can check [Angular Documentation](https://angular.io/api/router/TitleStrategy) to write a custom **`TitleStrategy`**. diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 8abc246509..d30322e277 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -1188,6 +1188,10 @@ { "text": "Abp Window Service", "path": "UI/Angular/Abp-Window-Service.md" + }, + { + "text": "Title Strategy", + "path": "UI/Angular/Title-Strategy-Service.md" } ] }, diff --git a/docs/pt-BR/AspNetCore/Auto-API-Controllers.md b/docs/pt-BR/AspNetCore/Auto-API-Controllers.md new file mode 100644 index 0000000000..01c62bab94 --- /dev/null +++ b/docs/pt-BR/AspNetCore/Auto-API-Controllers.md @@ -0,0 +1,3 @@ +Este documento foi movido. + +[Clique para navegar até o documento Auto API Controllers](../API/Auto-API-Controllers.md) \ No newline at end of file diff --git a/docs/pt-BR/AspNetCore/Bundling-Minification.md b/docs/pt-BR/AspNetCore/Bundling-Minification.md new file mode 100644 index 0000000000..48b33d2ee2 --- /dev/null +++ b/docs/pt-BR/AspNetCore/Bundling-Minification.md @@ -0,0 +1,3 @@ +Este documento foi movido. + +[Clique para navegar até o documento de Bundling & Minification do ASP.NET Core MVC](../UI/AspNetCore/Bundling-Minification.md) \ No newline at end of file diff --git a/docs/pt-BR/AspNetCore/Client-Side-Package-Management.md b/docs/pt-BR/AspNetCore/Client-Side-Package-Management.md new file mode 100644 index 0000000000..9d90d3407d --- /dev/null +++ b/docs/pt-BR/AspNetCore/Client-Side-Package-Management.md @@ -0,0 +1,3 @@ +Este documento foi movido. + +[Clique para navegar até o documento de Gerenciamento de Pacotes do Lado do Cliente do ASP.NET Core MVC](../UI/AspNetCore/Client-Side-Package-Management.md) \ No newline at end of file diff --git a/docs/pt-BR/AspNetCore/Dynamic-CSharp-API-Clients.md b/docs/pt-BR/AspNetCore/Dynamic-CSharp-API-Clients.md new file mode 100644 index 0000000000..80ba2f16d3 --- /dev/null +++ b/docs/pt-BR/AspNetCore/Dynamic-CSharp-API-Clients.md @@ -0,0 +1,3 @@ +Este documento foi movido. + +[Clique para navegar até o documento Dynamic C# API Clients](../API/Dynamic-CSharp-API-Clients.md) \ No newline at end of file diff --git a/docs/pt-BR/AspNetCore/Tag-Helpers/Dynamic-Forms.md b/docs/pt-BR/AspNetCore/Tag-Helpers/Dynamic-Forms.md new file mode 100644 index 0000000000..8f1d7e0fed --- /dev/null +++ b/docs/pt-BR/AspNetCore/Tag-Helpers/Dynamic-Forms.md @@ -0,0 +1,3 @@ +Este documento foi movido. + +[Clique para navegar até o documento de Formulários Dinâmicos](../../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md) \ No newline at end of file diff --git a/docs/pt-BR/AspNetCore/Tag-Helpers/Index.md b/docs/pt-BR/AspNetCore/Tag-Helpers/Index.md new file mode 100644 index 0000000000..83137c23e7 --- /dev/null +++ b/docs/pt-BR/AspNetCore/Tag-Helpers/Index.md @@ -0,0 +1,3 @@ +Este documento foi movido. + +[Clique para navegar até o documento ABP Tag Helpers](../../UI/AspNetCore/Tag-Helpers/Index.md) \ No newline at end of file diff --git a/docs/pt-BR/AspNetCore/Theming.md b/docs/pt-BR/AspNetCore/Theming.md new file mode 100644 index 0000000000..59127bed3d --- /dev/null +++ b/docs/pt-BR/AspNetCore/Theming.md @@ -0,0 +1,3 @@ +Este documento foi movido. + +[Clique para navegar até o documento de Temas](../UI/AspNetCore/Theming.md) \ No newline at end of file diff --git a/docs/pt-BR/AspNetCore/Widgets.md b/docs/pt-BR/AspNetCore/Widgets.md new file mode 100644 index 0000000000..6d2e611c9b --- /dev/null +++ b/docs/pt-BR/AspNetCore/Widgets.md @@ -0,0 +1,3 @@ +Este documento foi movido. + +[Clique para navegar até o documento Widgets](../UI/AspNetCore/Widgets.md) \ No newline at end of file diff --git a/docs/pt-BR/Audit-Logging.md b/docs/pt-BR/Audit-Logging.md index 609565d6f5..65e1271012 100644 --- a/docs/pt-BR/Audit-Logging.md +++ b/docs/pt-BR/Audit-Logging.md @@ -1,3 +1,390 @@ -# Audit Logging +# Registro de Auditoria -Façam \ No newline at end of file +[Wikipedia](https://en.wikipedia.org/wiki/Audit_trail): "*Um rastro de auditoria (também chamado de **log de auditoria**) é um registro cronológico relevante para a segurança, conjunto de registros e/ou destino e origem de registros que fornecem evidências documentais da sequência de atividades que afetaram a qualquer momento uma operação, procedimento ou evento específico*". + +O ABP Framework fornece um **sistema de registro de auditoria extensível** que automatiza o registro de auditoria por **convenção** e fornece **pontos de configuração** para controlar o nível dos logs de auditoria. + +Um **objeto de log de auditoria** (consulte a seção Objeto de Log de Auditoria abaixo) é tipicamente criado e salvo por solicitação da web. Ele inclui; + +* Detalhes da **solicitação e resposta** (como URL, método Http, informações do navegador, código de status HTTP... etc.). +* **Ações realizadas** (ações do controlador e chamadas de métodos de serviço de aplicação com seus parâmetros). +* **Mudanças de entidade** ocorridas na solicitação da web. +* Informações de **exceção** (se houve um erro durante a execução da solicitação). +* **Duração da solicitação** (para medir o desempenho da aplicação). + +> Os [modelos de inicialização](Startup-Templates/Index.md) são configurados para o sistema de registro de auditoria, o que é adequado para a maioria das aplicações. Use este documento para um controle detalhado sobre o sistema de log de auditoria. + +### Suporte do Provedor de Banco de Dados + +* Totalmente suportado pelo provedor [Entity Framework Core](Entity-Framework-Core.md). +* O log de alterações de entidade não é suportado pelo provedor [MongoDB](MongoDB.md). Outros recursos funcionam conforme o esperado. + +## UseAuditing() + +O middleware `UseAuditing()` deve ser adicionado ao pipeline de solicitações do ASP.NET Core para criar e salvar os logs de auditoria. Se você criou suas aplicações usando [os modelos de inicialização](Startup-Templates/Index.md), ele já está adicionado. + +## AbpAuditingOptions + +`AbpAuditingOptions` é o principal [objeto de opções](Options.md) para configurar o sistema de log de auditoria. Você pode configurá-lo no método `ConfigureServices` do seu [módulo](Module-Development-Basics.md): + +````csharp +Configure<AbpAuditingOptions>(options => +{ + options.IsEnabled = false; //Desativa o sistema de auditoria +}); +```` + +Aqui, uma lista das opções que você pode configurar: + +* `IsEnabled` (padrão: `true`): Uma chave raiz para habilitar ou desabilitar o sistema de auditoria. Outras opções não são usadas se esse valor for `false`. +* `HideErrors` (padrão: `true`): O sistema de log de auditoria oculta e escreve [logs](Logging.md) regulares se ocorrer algum erro ao salvar os objetos de log de auditoria. Se salvar os logs de auditoria for crítico para o seu sistema, defina isso como `false` para lançar uma exceção em caso de ocultação de erros. +* `IsEnabledForAnonymousUsers` (padrão: `true`): Se você deseja escrever logs de auditoria apenas para os usuários autenticados, defina isso como `false`. Se você salvar logs de auditoria para usuários anônimos, verá `null` para os valores de `UserId` desses usuários. +* `AlwaysLogOnException` (padrão: `true`): Se definido como verdadeiro, sempre salva o log de auditoria em caso de exceção/erro sem verificar outras opções (exceto `IsEnabled`, que desativa completamente o registro de auditoria). +* `IsEnabledForIntegrationService` (padrão: `false`): O Registro de Auditoria é desativado para [serviços de integração](Integration-Services.md) por padrão. Defina essa propriedade como `true` para habilitá-la. +* `IsEnabledForGetRequests` (padrão: `false`): As solicitações HTTP GET normalmente não devem fazer nenhuma alteração no banco de dados e o sistema de log de auditoria não salva objetos de log de auditoria para solicitações GET. Defina isso como `true` para habilitá-lo também para as solicitações GET. +* `DisableLogActionInfo` (padrão: `false`): Se definido como verdadeiro, não registrará mais `AuditLogActionInfo`. +* `ApplicationName`: Se várias aplicações estiverem salvando logs de auditoria em um único banco de dados, defina essa propriedade com o nome da sua aplicação, para que você possa distinguir os logs de diferentes aplicações. Se você não definir, ele será definido a partir do valor `IApplicationInfoAccessor.ApplicationName`, que é o nome da assembly de entrada por padrão. +* `IgnoredTypes`: Uma lista de `Type`s a serem ignorados para o registro de auditoria. Se for um tipo de entidade, as alterações para esse tipo de entidades não serão salvas. Esta lista também é usada ao serializar os parâmetros de ação. +* `EntityHistorySelectors`: Uma lista de seletores usados para determinar se um tipo de entidade é selecionado para salvar a alteração da entidade. Consulte a seção abaixo para detalhes. +* `SaveEntityHistoryWhenNavigationChanges` (padrão: `true`): Se definido como verdadeiro, salvará as alterações da entidade no log de auditoria quando houver alterações em propriedades de navegação. +* `Contributors`: Uma lista de implementações de `AuditLogContributor`. Um contribuidor é uma forma de estender o sistema de log de auditoria. Consulte a seção "Contribuidores de Log de Auditoria" abaixo. +* `AlwaysLogSelectors`: Uma lista de seletores para salvar os logs de auditoria para os critérios correspondentes. + +### Seletores de Histórico de Entidade + +Salvar todas as alterações de todas as suas entidades exigiria muito espaço no banco de dados. Por esse motivo, **o sistema de log de auditoria não salva nenhuma alteração para as entidades a menos que você configure explicitamente**. + +Para salvar todas as alterações de todas as entidades, simplesmente use o método de extensão `AddAllEntities()`. + +````csharp +Configure<AbpAuditingOptions>(options => +{ + options.EntityHistorySelectors.AddAllEntities(); +}); +```` + +`options.EntityHistorySelectors` na verdade é uma lista de predicados de tipo. Você pode escrever uma expressão lambda para definir seu filtro. + +O seletor de exemplo abaixo faz o mesmo do método de extensão `AddAllEntities()` definido acima: + +````csharp +Configure<AbpAuditingOptions>(options => +{ + options.EntityHistorySelectors.Add( + new NamedTypeSelector( + "MeuSeletorNome", + type => + { + if (typeof(IEntity).IsAssignableFrom(type)) + { + return true; + } + else + { + return false; + } + } + ) + ); +}); +```` + +A condição `typeof(IEntity).IsAssignableFrom(type)` será `true` para qualquer classe que implemente a interface `IEntity` (tecnicamente, todas as entidades em sua aplicação). Você pode verificar condicionalmente e retornar `true` ou `false` com base em sua preferência. + +`options.EntityHistorySelectors` é uma forma flexível e dinâmica de selecionar as entidades para o registro de auditoria. Outra forma é usar os atributos `Audited` e `DisableAuditing` por entidade. + +## AbpAspNetCoreAuditingOptions + +`AbpAspNetCoreAuditingOptions` é o [objeto de opções](Options.md) para configurar o registro de auditoria na camada ASP.NET Core. Você pode configurá-lo no método `ConfigureServices` do seu [módulo](Module-Development-Basics.md): + +````csharp +Configure<AbpAspNetCoreAuditingOptions>(options => +{ + options.IgnoredUrls.Add("/produtos"); +}); +```` + +`IgnoredUrls` é a única opção. É uma lista de prefixos de URLs ignorados. No exemplo anterior, todas as URLs que começam com `/produtos` serão ignoradas para o registro de auditoria. + +## Habilitando/Desabilitando o Registro de Auditoria para Serviços + +### Habilitar/Desabilitar para Controladores e Ações + +Todas as ações do controlador são registradas por padrão (consulte `IsEnabledForGetRequests` acima para solicitações GET). + +Você pode usar o `[DisableAuditing]` para desativá-lo para um tipo de controlador específico: + +````csharp +[DisableAuditing] +public class HomeController : AbpController +{ + //... +} +```` + +Use `[DisableAuditing]` para qualquer ação para controlá-la no nível da ação: + +````csharp +public class HomeController : AbpController +{ + [DisableAuditing] + public async Task<ActionResult> Home() + { + //... + } + + public async Task<ActionResult> OutraAcaoRegistrada() + { + //... + } +} +```` + +### Habilitar/Desabilitar para Serviços de Aplicação e Métodos + +As chamadas de métodos de [serviço de aplicação](Application-Services.md) também são incluídas no log de auditoria por padrão. Você pode usar o `[DisableAuditing]` no nível do serviço ou do método. + +#### Habilitar/Desabilitar para Outros Serviços + +O registro de auditoria de ação pode ser habilitado para qualquer tipo de classe (registrado e resolvido da [injeção de dependência](Dependency-Injection.md)) enquanto é habilitado apenas para os controladores e os serviços de aplicação por padrão. + +Use `[Audited]` e `[DisableAuditing]` para qualquer classe ou método que precisa ser registrado no log de auditoria. Além disso, sua classe pode (direta ou implicitamente) implementar a interface `IAuditingEnabled` para habilitar o registro de auditoria para essa classe por padrão. + +### Habilitar/Desabilitar para Entidades e Propriedades + +Uma entidade é ignorada no registro de alteração de entidade nos seguintes casos; + +* Se você adicionar um tipo de entidade às `AbpAuditingOptions.IgnoredTypes` (como explicado anteriormente), ele é completamente ignorado no sistema de registro de auditoria. +* Se o objeto não for uma [entidade](Entities.md) (não implementa `IEntity` diretamente ou implicitamente - Todas as entidades implementam essa interface por padrão). +* Se o tipo de entidade não for público. + +Caso contrário, você pode usar `Audited` para habilitar o registro de alteração de entidade para uma entidade: + +````csharp +[Audited] +public class MinhaEntidade : Entity<Guid> +{ + //... +} +```` + +Ou desativá-lo para uma entidade: + +````csharp +[DisableAuditing] +public class MinhaEntidade : Entity<Guid> +{ + //... +} +```` + +Desativar o registro de auditoria pode ser necessário apenas se a entidade estiver sendo selecionada pelos `AbpAuditingOptions.EntityHistorySelectors` que foram explicados anteriormente. + +Você pode desativar o registro de auditoria apenas para algumas propriedades de suas entidades para um controle detalhado sobre o registro de auditoria: + +````csharp +[Audited] +public class MeuUsuario : Entity<Guid> +{ + public string Nome { get; set; } + + public string Email { get; set; } + + [DisableAuditing] //Ignora a Senha no registro de auditoria + public string Senha { get; set; } +} +```` + +O sistema de log de auditoria salvará as alterações para a entidade `MeuUsuario` enquanto ignora a propriedade `Senha`, que pode ser perigosa de salvar por motivos de segurança. + +Em alguns casos, você pode querer salvar apenas algumas propriedades de suas entidades e ignorar todas as outras. Escrever `[DisableAuditing]` para todas as outras propriedades seria tedioso. Em tais casos, use `[Audited]` apenas para as propriedades desejadas e marque a entidade com o atributo `[DisableAuditing]`: + +````csharp +[DisableAuditing] +public class MeuUsuario : Entity<Guid> +{ + [Audited] //Apenas registra a alteração do Nome + public string Nome { get; set; } + + public string Email { get; set; } + + public string Senha { get; set; } +} +```` +## IAuditingStore + +`IAuditingStore` é uma interface usada para salvar os objetos de log de auditoria (explicados abaixo) pelo Framework ABP. Se você precisa salvar os objetos de log de auditoria em um armazenamento de dados personalizado, pode implementar o `IAuditingStore` em sua própria aplicação e substituir usando o [sistema de injeção de dependência](Dependency-Injection.md). + +`SimpleLogAuditingStore` é usado se nenhum armazenamento de auditoria estiver registrado. Ele simplesmente escreve o objeto de auditoria no sistema padrão de [logging](Logging.md). + +[O Módulo de Registro de Auditoria](Modules/Audit-Logging.md) foi configurado nos [modelos de inicialização](Startup-Templates/Index.md) para salvar objetos de log de auditoria em um banco de dados (ele suporta vários provedores de banco de dados). Portanto, na maioria das vezes, você não precisa se preocupar com como o `IAuditingStore` foi implementado e usado. + +## Objeto de Log de Auditoria + +Um **objeto de log de auditoria** é criado para cada **solicitação web** por padrão. Um objeto de log de auditoria pode ser representado pelo seguinte diagrama de relação: + +![**auditlog-object-diagram**](images/auditlog-object-diagram.png) + +* **AuditLogInfo**: O objeto raiz com as seguintes propriedades: + * `ApplicationName`: Quando você salva logs de auditoria de diferentes aplicações no mesmo banco de dados, essa propriedade é usada para distinguir os logs das aplicações. + * `UserId`: Id do usuário atual, se o usuário estiver logado. + * `UserName`: Nome do usuário atual, se o usuário estiver logado (esse valor está aqui para não depender do módulo/sistema de identidade para pesquisa). + * `TenantId`: Id do locatário atual, para uma aplicação multi-locatário. + * `TenantName`: Nome do locatário atual, para uma aplicação multi-locatário. + * `ExecutionTime`: O momento em que este objeto de log de auditoria foi criado. + * `ExecutionDuration`: Duração total da execução da solicitação, em milissegundos. Isso pode ser usado para observar o desempenho da aplicação. + * `ClientId`: Id do cliente atual, se o cliente estiver autenticado. Um cliente é geralmente uma aplicação de terceiros que usa o sistema por meio de uma API HTTP. + * `ClientName`: Nome do cliente atual, se disponível. + * `ClientIpAddress`: Endereço IP do cliente/dispositivo do usuário. + * `CorrelationId`: Id de [Correlação Atual](CorrelationId.md). O Id de correlação é usado para relacionar os logs de auditoria escritos por diferentes aplicações (ou microsserviços) em uma única operação lógica. + * `BrowserInfo`: Informações do nome/versão do navegador do usuário atual, se disponível. + * `HttpMethod`: Método HTTP da solicitação atual (GET, POST, PUT, DELETE... etc.). + * `HttpStatusCode`: Código de status da resposta HTTP para esta solicitação. + * `Url`: URL da solicitação. +* **AuditLogActionInfo**: Um log de auditoria de ação é tipicamente uma ação de controlador ou uma chamada de método de [serviço de aplicação](Application-Services.md) durante a solicitação web. Um log de ação pode conter várias ações. Um objeto de ação tem as seguintes propriedades: + * `ServiceName`: Nome do controlador/serviço executado. + * `MethodName`: Nome do método executado do controlador/serviço. + * `Parameters`: Um texto formatado em JSON representando os parâmetros passados para o método. + * `ExecutionTime`: O momento em que este método foi executado. + * `ExecutionDuration`: Duração da execução do método, em milissegundos. Isso pode ser usado para observar o desempenho do método. +* **EntityChangeInfo**: Representa uma alteração de uma entidade nesta solicitação web. Um log de auditoria pode conter zero ou mais alterações de entidade. Uma alteração de entidade tem as seguintes propriedades: + * `ChangeTime`: O momento em que a entidade foi alterada. + * `ChangeType`: Um enum com os seguintes campos: `Criado` (0), `Atualizado` (1) e `Excluído` (2). + * `EntityId`: Id da entidade que foi alterada. + * `EntityTenantId`: Id do locatário a que esta entidade pertence. + * `EntityTypeFullName`: Nome do tipo (classe) da entidade com namespace completo (como *Acme.BookStore.Book* para a entidade Book). +* **EntityPropertyChangeInfo**: Representa uma alteração de uma propriedade de uma entidade. Uma informação de alteração de entidade (explicada acima) pode conter uma ou mais alterações de propriedade com as seguintes propriedades: + * `NewValue`: Novo valor da propriedade. É `null` se a entidade foi excluída. + * `OriginalValue`: Valor antigo/original antes da alteração. É `null` se a entidade foi recém-criada. + * `PropertyName`: O nome da propriedade na classe da entidade. + * `PropertyTypeFullName`: Nome do tipo (classe) da propriedade com namespace completo. +* **Exception**: Um objeto de log de auditoria pode conter zero ou mais exceções. Dessa forma, você pode obter um relatório das solicitações com falha. +* **Comment**: Um valor de string arbitrário para adicionar mensagens personalizadas à entrada de log de auditoria. Um objeto de log de auditoria pode conter zero ou mais comentários. + +Além das propriedades padrão explicadas acima, os objetos `AuditLogInfo`, `AuditLogActionInfo` e `EntityChangeInfo` implementam a interface `IHasExtraProperties`, para que você possa adicionar propriedades personalizadas a esses objetos. + +## Contribuidores de Log de Auditoria + +Você pode estender o sistema de auditoria criando uma classe derivada da classe `AuditLogContributor`, que define os métodos `PreContribute` e `PostContribute`. + +O único contribuidor pré-construído é a classe `AspNetCoreAuditLogContributor`, que define as propriedades relacionadas a uma solicitação HTTP. + +Um contribuidor pode definir propriedades e coleções da classe `AuditLogInfo` para adicionar mais informações. + +Exemplo: + +````csharp +public class MyAuditLogContributor : AuditLogContributor +{ + public override void PreContribute(AuditLogContributionContext context) + { + var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>(); + context.AuditInfo.SetProperty( + "MyCustomClaimValue", + currentUser.FindClaimValue("MyCustomClaim") + ); + } + + public override void PostContribute(AuditLogContributionContext context) + { + context.AuditInfo.Comments.Add("Algum comentário..."); + } +} +```` + +* `context.ServiceProvider` pode ser usado para resolver serviços da [injeção de dependência](Dependency-Injection.md). +* `context.AuditInfo` pode ser usado para acessar o objeto de log de auditoria atual para manipulá-lo. + +Após criar um contribuidor, você deve adicioná-lo à lista `AbpAuditingOptions.Contributors`: + +````csharp +Configure<AbpAuditingOptions>(options => +{ + options.Contributors.Add(new MyAuditLogContributor()); +}); +```` + +## IAuditLogScope & IAuditingManager + +Esta seção explica os serviços `IAuditLogScope` e `IAuditingManager` para casos de uso avançados. + +Um **escopo de log de auditoria** é um [escopo ambiente](Ambient-Context-Pattern.md) que **constrói** e **salva** um objeto de log de auditoria (explicado anteriormente). Por padrão, um escopo de log de auditoria é criado para uma solicitação web pelo Middleware de Log de Auditoria (veja a seção `UseAuditing()` acima). + +### Acesso ao Escopo Atual de Log de Auditoria + +Os contribuidores de log de auditoria, explicados acima, são uma maneira global de manipular o objeto de log de auditoria. É bom se você puder obter um valor de um serviço. + +Se você precisar manipular o objeto de log de auditoria em um ponto arbitrário de sua aplicação, pode acessar o escopo de log de auditoria atual e obter o objeto de log de auditoria atual (independente de como o escopo é gerenciado). Exemplo: + +````csharp +public class MeuServico : ITransientDependency +{ + private readonly IAuditingManager _auditingManager; + + public MeuServico(IAuditingManager auditingManager) + { + _auditingManager = auditingManager; + } + + public async Task FazerIssoAsync() + { + var escopoAtualDeLogDeAuditoria = _auditingManager.Current; + if (escopoAtualDeLogDeAuditoria != null) + { + escopoAtualDeLogDeAuditoria.Log.Comments.Add( + "Executou o método MeuServico.FazerIssoAsync :)" + ); + + escopoAtualDeLogDeAuditoria.Log.SetProperty("MinhaPropriedadePersonalizada", 42); + } + } +} +```` + +Sempre verifique se `_auditingManager.Current` é nulo ou não, porque é controlado em um escopo externo e você não pode saber se um escopo de log de auditoria foi criado antes de chamar seu método. + +### Criar Manualmente um Escopo de Log de Auditoria + +Raramente você precisa criar manualmente um escopo de log de auditoria, mas se precisar, pode criar um escopo de log de auditoria usando o `IAuditingManager` como no exemplo a seguir: + +````csharp +public class MeuServico : ITransientDependency +{ + private readonly IAuditingManager _auditingManager; + + public MeuServico(IAuditingManager auditingManager) + { + _auditingManager = auditingManager; + } + + public async Task FazerIssoAsync() + { + using (var escopoDeAuditoria = _auditingManager.BeginScope()) + { + try + { + //Chame outros serviços... + } + catch (Exception ex) + { + //Adicione exceções + _auditingManager.Current.Log.Exceptions.Add(ex); + throw; + } + finally + { + //Sempre salve o log + await escopoDeAuditoria.SaveAsync(); + } + } + } +} +```` + +Você pode chamar outros serviços, que podem chamar outros, que podem alterar entidades e assim por diante. Todas essas interações são salvas como um único objeto de log de auditoria no bloco finally. + +## O Módulo de Registro de Auditoria + +O Módulo de Registro de Auditoria basicamente implementa o `IAuditingStore` para salvar os objetos de log de auditoria em um banco de dados. Ele suporta vários provedores de banco de dados. Este módulo é adicionado aos modelos de inicialização por padrão. + +Consulte o documento [Módulo de Registro de Auditoria](Modules/Audit-Logging.md) para mais informações. diff --git a/docs/pt-BR/Authorization.md b/docs/pt-BR/Authorization.md index 941666a9bf..326b596388 100644 --- a/docs/pt-BR/Authorization.md +++ b/docs/pt-BR/Authorization.md @@ -1,3 +1,359 @@ -## Authorization +# Autorização -Façam \ No newline at end of file +A autorização é usada para verificar se um usuário tem permissão para realizar operações específicas na aplicação. + +O ABP estende a [Autorização do ASP.NET Core](https://docs.microsoft.com/pt-br/aspnet/core/security/authorization/introduction) adicionando **permissões** como [políticas](https://docs.microsoft.com/pt-br/aspnet/core/security/authorization/policies) automáticas e permitindo que o sistema de autorização seja utilizado nos **[serviços de aplicação](Application-Services.md)** também. + +Portanto, todos os recursos de autorização do ASP.NET Core e a documentação são válidos em uma aplicação baseada no ABP. Este documento se concentra nos recursos adicionados ao sistema de autorização do ASP.NET Core. + +## Atributo Authorize + +O ASP.NET Core define o atributo [**Authorize**](https://docs.microsoft.com/pt-br/aspnet/core/security/authorization/simple) que pode ser usado para uma ação, um controlador ou uma página. O ABP permite que você use o mesmo atributo para um [serviço de aplicação](Application-Services.md). + +Exemplo: + +```csharp +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Services; + +namespace Acme.BookStore +{ + [Authorize] + public class AuthorAppService : ApplicationService, IAuthorAppService + { + public Task<List<AuthorDto>> GetListAsync() + { + ... + } + + [AllowAnonymous] + public Task<AuthorDto> GetAsync(Guid id) + { + ... + } + + [Authorize("BookStore_Author_Create")] + public Task CreateAsync(CreateAuthorDto input) + { + ... + } + } +} + +``` + +- O atributo `Authorize` obriga o usuário a fazer login na aplicação para usar os métodos do `AuthorAppService`. Portanto, o método `GetListAsync` está disponível apenas para usuários autenticados. +- `AllowAnonymous` suprime a autenticação. Portanto, o método `GetAsync` está disponível para todos, incluindo usuários não autorizados. +- `[Authorize("BookStore_Author_Create")]` define uma política (consulte [autorização baseada em políticas](https://docs.microsoft.com/pt-br/aspnet/core/security/authorization/policies)) que é verificada para autorizar o usuário atual. + +"BookStore_Author_Create" é um nome de política arbitrário. Se você declarar um atributo como esse, o sistema de autorização do ASP.NET Core espera que uma política seja definida anteriormente. + +Você pode, é claro, implementar suas próprias políticas conforme descrito na documentação do ASP.NET Core. Mas para condições simples de verdadeiro/falso, como se uma política foi concedida a um usuário ou não, o ABP define o sistema de permissões, que será explicado na próxima seção. + +## Sistema de Permissões + +Uma permissão é uma política simples que é concedida ou proibida para um usuário, função ou cliente específico. + +### Definindo Permissões + +Para definir permissões, crie uma classe que herde de `PermissionDefinitionProvider`, conforme mostrado abaixo: + +```csharp +using Volo.Abp.Authorization.Permissions; + +namespace Acme.BookStore.Permissions +{ + public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var myGroup = context.AddGroup("BookStore"); + + myGroup.AddPermission("BookStore_Author_Create"); + } + } +} +``` + +> O ABP descobre automaticamente essa classe. Nenhuma configuração adicional é necessária! + +> Normalmente, você define essa classe dentro do projeto `Application.Contracts` da sua [aplicação](Startup-Templates/Application.md). O modelo de inicialização já vem com uma classe vazia chamada *YourProjectNamePermissionDefinitionProvider* com a qual você pode começar. + +No método `Define`, você precisa adicionar um **grupo de permissões** ou obter um grupo existente e adicionar **permissões** a esse grupo. + +Quando você define uma permissão, ela se torna utilizável no sistema de autorização do ASP.NET Core como um nome de **política**. Ela também se torna visível na interface do usuário. Veja o diálogo de permissões para uma função: + +![authorization-new-permission-ui](images/authorization-new-permission-ui.png) + +- O grupo "BookStore" é mostrado como uma nova guia no lado esquerdo. +- "BookStore_Author_Create" no lado direito é o nome da permissão. Você pode concedê-la ou proibi-la para a função. + +Quando você salva o diálogo, ele é salvo no banco de dados e usado no sistema de autorização. + +> A tela acima está disponível quando você instalou o módulo de identidade, que é usado principalmente para gerenciamento de usuários e funções. Os modelos de inicialização já vêm com o módulo de identidade pré-instalado. + +#### Localizando o Nome da Permissão + +"BookStore_Author_Create" não é um bom nome de permissão para a interface do usuário. Felizmente, os métodos `AddPermission` e `AddGroup` podem receber um `LocalizableString` como segundo parâmetro: + +```csharp +var myGroup = context.AddGroup( + "BookStore", + LocalizableString.Create<BookStoreResource>("BookStore") +); + +myGroup.AddPermission( + "BookStore_Author_Create", + LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create") +); +``` + +Em seguida, você pode definir os textos para as chaves "BookStore" e "Permission:BookStore_Author_Create" no arquivo de localização: + +```json +"BookStore": "Livraria", +"Permission:BookStore_Author_Create": "Criar um novo autor" +``` + +> Para mais informações, consulte a [documentação de localização](Localization.md) sobre o sistema de localização. + +A interface do usuário localizada será como mostrado abaixo: + +![authorization-new-permission-ui-localized](images/authorization-new-permission-ui-localized.png) + +#### Multi-Tenancy + +O ABP suporta [multi-tenancy](Multi-Tenancy.md) como um recurso de primeira classe. Você pode definir a opção de lado de multi-tenancy ao definir uma nova permissão. Ela pode ter um dos três valores definidos abaixo: + +- **Host**: A permissão está disponível apenas para o lado do host. +- **Tenant**: A permissão está disponível apenas para o lado do tenant. +- **Ambos** (padrão): A permissão está disponível tanto para o lado do tenant quanto para o lado do host. + +> Se sua aplicação não é multi-tenant, você pode ignorar essa opção. + +Para definir a opção de lado de multi-tenancy, passe para o terceiro parâmetro do método `AddPermission`: + +```csharp +myGroup.AddPermission( + "BookStore_Author_Create", + LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create"), + multiTenancySide: MultiTenancySides.Tenant //defina o lado de multi-tenancy! +); +``` + +#### Habilitar/Desabilitar Permissões + +Uma permissão está habilitada por padrão. É possível desabilitar uma permissão. Uma permissão desabilitada será proibida para todos. Você ainda pode verificar a permissão, mas ela sempre retornará proibida. + +Exemplo de definição: + +````csharp +myGroup.AddPermission("Author_Management", isEnabled: false); +```` + +Normalmente, você não precisa definir uma permissão desabilitada (a menos que queira desabilitar temporariamente um recurso da sua aplicação). No entanto, você pode querer desabilitar uma permissão definida em um módulo dependente. Dessa forma, você pode desabilitar a funcionalidade relacionada à aplicação. Consulte a seção "*Alterando as Definições de Permissão de um Módulo Dependente*" abaixo para um exemplo de uso. + +> Observação: Verificar uma permissão não definida lançará uma exceção, enquanto verificar uma permissão desabilitada simplesmente retornará proibida (falso). + +#### Permissões Filhas + +Uma permissão pode ter permissões filhas. Isso é especialmente útil quando você deseja criar uma árvore de permissões hierárquica, onde uma permissão pode ter permissões secundárias adicionais que estão disponíveis apenas se a permissão pai for concedida. + +Exemplo de definição: + +```csharp +var authorManagement = myGroup.AddPermission("Author_Management"); +authorManagement.AddChild("Author_Management_Create_Books"); +authorManagement.AddChild("Author_Management_Edit_Books"); +authorManagement.AddChild("Author_Management_Delete_Books"); +``` + +O resultado na interface do usuário é mostrado abaixo (provavelmente você desejará localizar as permissões para sua aplicação): + +![authorization-new-permission-ui-hierarcy](images/authorization-new-permission-ui-hierarcy.png) + +Para o código de exemplo, é assumido que uma função/usuário com a permissão "Author_Management" concedida pode ter permissões adicionais. Em seguida, um serviço de aplicação típico que verifica permissões pode ser definido como mostrado abaixo: + +```csharp +[Authorize("Author_Management")] +public class AuthorAppService : ApplicationService, IAuthorAppService +{ + public Task<List<AuthorDto>> GetListAsync() + { + ... + } + + public Task<AuthorDto> GetAsync(Guid id) + { + ... + } + + [Authorize("Author_Management_Create_Books")] + public Task CreateAsync(CreateAuthorDto input) + { + ... + } + + [Authorize("Author_Management_Edit_Books")] + public Task UpdateAsync(CreateAuthorDto input) + { + ... + } + + [Authorize("Author_Management_Delete_Books")] + public Task DeleteAsync(CreateAuthorDto input) + { + ... + } +} +``` + +- `GetListAsync` e `GetAsync` estarão disponíveis para usuários se a permissão `Author_Management` for concedida. +- Outros métodos requerem permissões adicionais. + +### Substituindo uma Permissão por uma Política Personalizada + +Se você definir e registrar uma política no sistema de autorização do ASP.NET Core com o mesmo nome de uma permissão, sua política substituirá a permissão existente. Isso é uma maneira poderosa de estender a autorização para um módulo pré-construído que você está usando em sua aplicação. + +Consulte o documento [autorização baseada em políticas](https://docs.microsoft.com/pt-br/aspnet/core/security/authorization/policies) para aprender como definir uma política personalizada. + +### Alterando as Definições de Permissão de um Módulo Dependente + +Uma classe derivada de `PermissionDefinitionProvider` (assim como o exemplo acima) também pode obter definições de permissão existentes (definidas pelos [módulos](Module-Development-Basics.md) dependentes) e alterar suas definições. + +Exemplo: + +````csharp +context + .GetPermissionOrNull(IdentityPermissions.Roles.Delete) + .IsEnabled = false; +```` + +Quando você escreve esse código dentro do seu provedor de definição de permissão, ele encontra a permissão de "exclusão de função" do [Módulo de Identidade](Modules/Identity.md) e desabilita a permissão, para que ninguém possa excluir uma função na aplicação. + +> Dica: É melhor verificar o valor retornado pelo método `GetPermissionOrNull`, pois ele pode retornar nulo se a permissão fornecida não foi definida. + +### Provedores de Valor de Permissão + +O sistema de verificação de permissões é extensível. Qualquer classe derivada de `PermissionValueProvider` (ou que implemente `IPermissionValueProvider`) pode contribuir para a verificação de permissões. Existem três provedores de valor predefinidos: + +- `UserPermissionValueProvider` verifica se o usuário atual tem a permissão concedida. Ele obtém o ID do usuário das reivindicações atuais. O nome da reivindicação do usuário é definido pela propriedade estática `AbpClaimTypes.UserId`. +- `RolePermissionValueProvider` verifica se algum dos papéis do usuário atual tem a permissão concedida. Ele obtém os nomes dos papéis das reivindicações atuais. O nome das reivindicações de papéis é definido pela propriedade estática `AbpClaimTypes.Role`. +- `ClientPermissionValueProvider` verifica se o cliente atual tem a permissão concedida. Isso é especialmente útil em uma interação máquina a máquina, onde não há usuário atual. Ele obtém o ID do cliente das reivindicações atuais. O nome da reivindicação do cliente é definido pela propriedade estática `AbpClaimTypes.ClientId`. + +Você pode estender o sistema de verificação de permissões definindo seu próprio provedor de valor de permissão. + +Exemplo: + +```csharp +public class SystemAdminPermissionValueProvider : PermissionValueProvider +{ + public SystemAdminPermissionValueProvider(IPermissionStore permissionStore) + : base(permissionStore) + { + } + + public override string Name => "SystemAdmin"; + + public async override Task<PermissionGrantResult> + CheckAsync(PermissionValueCheckContext context) + { + if (context.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin") + { + return PermissionGrantResult.Granted; + } + + return PermissionGrantResult.Undefined; + } +} +``` + +Esse provedor permite que todas as permissões sejam concedidas a um usuário com uma reivindicação `User_Type` que tenha o valor `SystemAdmin`. É comum usar as reivindicações atuais e o `IPermissionStore` em um provedor de valor de permissão. + +Um provedor de valor de permissão deve retornar um dos seguintes valores do método `CheckAsync`: + +- `PermissionGrantResult.Granted` é retornado para conceder a permissão ao usuário. Se qualquer um dos provedores retornar `Granted`, o resultado será `Granted`, se nenhum outro provedor retornar `Prohibited`. +- `PermissionGrantResult.Prohibited` é retornado para proibir a permissão ao usuário. Se qualquer um dos provedores retornar `Prohibited`, o resultado será sempre `Prohibited`. Não importa o que os outros provedores retornem. +- `PermissionGrantResult.Undefined` é retornado se esse provedor de valor de permissão não puder decidir sobre o valor da permissão. Retorne isso para permitir que outros provedores verifiquem a permissão. + +Uma vez que um provedor é definido, ele deve ser adicionado às `AbpPermissionOptions`, como mostrado abaixo: + +```csharp +Configure<AbpPermissionOptions>(options => +{ + options.ValueProviders.Add<SystemAdminPermissionValueProvider>(); +}); +``` + +### Armazenamento de Permissões + +`IPermissionStore` é a única interface que precisa ser implementada para ler o valor das permissões de uma fonte de persistência, geralmente um sistema de banco de dados. O módulo de gerenciamento de permissões a implementa e é pré-instalado no modelo de inicialização da aplicação. Consulte a [documentação do módulo de gerenciamento de permissões](Modules/Permission-Management.md) para obter mais informações. + +### AlwaysAllowAuthorizationService + +`AlwaysAllowAuthorizationService` é uma classe usada para ignorar o serviço de autorização. Geralmente é usado em testes de integração, onde você pode querer desabilitar o sistema de autorização. + +Use o método de extensão `IServiceCollection.AddAlwaysAllowAuthorization()` para registrar o `AlwaysAllowAuthorizationService` no sistema de [injeção de dependência](Dependency-Injection.md): + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + context.Services.AddAlwaysAllowAuthorization(); +} +``` + +Isso já é feito para testes de integração do modelo de inicialização. + +### Fábrica de Claims Principal + +As reivindicações são elementos importantes da autenticação e autorização. O ABP usa o serviço `IAbpClaimsPrincipalFactory` para criar reivindicações na autenticação. Esse serviço foi projetado para ser extensível. Se você precisar adicionar suas próprias reivindicações ao ticket de autenticação, poderá implementar `IAbpClaimsPrincipalContributor` em sua aplicação. + +**Exemplo: Adicionar uma reivindicação `SocialSecurityNumber` e obtê-la:** + +```csharp +public class SocialSecurityNumberClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency +{ + public async Task ContributeAsync(AbpClaimsPrincipalContributorContext context) + { + var identity = context.ClaimsPrincipal.Identities.FirstOrDefault(); + var userId = identity?.FindUserId(); + if (userId.HasValue) + { + var userService = context.ServiceProvider.GetRequiredService<IUserService>(); //Seu serviço personalizado + var socialSecurityNumber = await userService.GetSocialSecurityNumberAsync(userId.Value); + if (socialSecurityNumber != null) + { + identity.AddClaim(new Claim("SocialSecurityNumber", socialSecurityNumber)); + } + } + } +} + + +public static class CurrentUserExtensions +{ + public static string GetSocialSecurityNumber(this ICurrentUser currentUser) + { + return currentUser.FindClaimValue("SocialSecurityNumber"); + } +} +``` + +> Se você estiver usando o Identity Server, adicione suas reivindicações a `RequestedClaims` de `AbpClaimsServiceOptions`. + +```csharp +Configure<AbpClaimsServiceOptions>(options => +{ + options.RequestedClaims.AddRange(new[]{ "SocialSecurityNumber" }); +}); +``` + +## Veja também + +* [Módulo de Gerenciamento de Permissões](Modules/Permission-Management.md) +* [API de Autenticação JavaScript do ASP.NET Core MVC / Razor Pages](UI/AspNetCore/JavaScript-API/Auth.md) +* [Gerenciamento de Permissões na Interface do Usuário Angular](UI/Angular/Permission-Management.md) +* [Tutorial em vídeo](https://abp.io/video-courses/essentials/authorization)</source> \ No newline at end of file diff --git a/docs/pt-BR/Background-Jobs-Quartz.md b/docs/pt-BR/Background-Jobs-Quartz.md new file mode 100644 index 0000000000..9f556c7626 --- /dev/null +++ b/docs/pt-BR/Background-Jobs-Quartz.md @@ -0,0 +1,159 @@ +# Gerenciador de Tarefas em Segundo Plano Quartz + +[Quartz](https://www.quartz-scheduler.net/) é um avançado gerenciador de tarefas em segundo plano. Você pode integrar o Quartz com o ABP Framework para usá-lo em vez do [gerenciador de tarefas em segundo plano padrão](Background-Jobs.md). Dessa forma, você pode usar a mesma API de tarefas em segundo plano para o Quartz e seu código será independente do Quartz. Se preferir, você também pode usar diretamente a API do Quartz. + +> Consulte o [documento de tarefas em segundo plano](Background-Jobs.md) para aprender como usar o sistema de tarefas em segundo plano. Este documento mostra apenas como instalar e configurar a integração com o Quartz. + +## Instalação + +É sugerido usar o [ABP CLI](CLI.md) para instalar este pacote. + +### Usando o ABP CLI + +Abra uma janela de linha de comando na pasta do projeto (arquivo .csproj) e digite o seguinte comando: + +````bash +abp add-package Volo.Abp.BackgroundJobs.Quartz +```` + +> Se você ainda não o fez, primeiro precisa instalar o [ABP CLI](CLI.md). Para outras opções de instalação, consulte [a página de descrição do pacote](https://abp.io/package-detail/Volo.Abp.BackgroundJobs.Quartz). + +### Instalação Manual + +Se você deseja instalar manualmente: + +1. Adicione o pacote NuGet [Volo.Abp.BackgroundJobs.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.Quartz) ao seu projeto: + + ```` + Install-Package Volo.Abp.BackgroundJobs.Quartz + ```` + +2. Adicione o `AbpBackgroundJobsQuartzModule` à lista de dependências do seu módulo: + +````csharp +[DependsOn( + //...outras dependências + typeof(AbpBackgroundJobsQuartzModule) //Adicione a nova dependência do módulo + )] +public class SeuModulo : AbpModule +{ +} +```` + +## Configuração + +O Quartz é uma biblioteca muito configurável e o framework ABP fornece `AbpQuartzOptions` para isso. Você pode usar o método `PreConfigure` na classe do seu módulo para pré-configurar essa opção. O ABP a usará ao inicializar o módulo Quartz. Por exemplo: + +````csharp +[DependsOn( + //...outras dependências + typeof(AbpBackgroundJobsQuartzModule) //Adicione a nova dependência do módulo + )] +public class SeuModulo : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + PreConfigure<AbpQuartzOptions>(options => + { + options.Properties = new NameValueCollection + { + ["quartz.jobStore.dataSource"] = "BackgroundJobsDemoApp", + ["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz", + ["quartz.jobStore.tablePrefix"] = "QRTZ_", + ["quartz.serializer.type"] = "json", + ["quartz.dataSource.BackgroundJobsDemoApp.connectionString"] = configuration.GetConnectionString("Quartz"), + ["quartz.dataSource.BackgroundJobsDemoApp.provider"] = "SqlServer", + ["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz", + }; + }); + } +} +```` + +A partir da versão 3.1 do ABP, adicionamos o `Configurator` ao `AbpQuartzOptions` para configurar o Quartz. Por exemplo: + +````csharp +[DependsOn( + //...outras dependências + typeof(AbpBackgroundJobsQuartzModule) //Adicione a nova dependência do módulo + )] +public class SeuModulo : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + PreConfigure<AbpQuartzOptions>(options => + { + options.Configurator = configure => + { + configure.UsePersistentStore(storeOptions => + { + storeOptions.UseProperties = true; + storeOptions.UseJsonSerializer(); + storeOptions.UseSqlServer(configuration.GetConnectionString("Quartz")); + storeOptions.UseClustering(c => + { + c.CheckinMisfireThreshold = TimeSpan.FromSeconds(20); + c.CheckinInterval = TimeSpan.FromSeconds(10); + }); + }); + }; + }); + } +} +```` + +> Você pode escolher a maneira que preferir para configurar o Quartz. + +O Quartz armazena informações de tarefas e agendamento **em memória por padrão**. No exemplo, usamos a pré-configuração do [padrão de opções](Options.md) para alterá-lo para o banco de dados. Para mais configurações do Quartz, consulte a [documentação do Quartz](https://www.quartz-scheduler.net/). + +## Tratamento de Exceções + +### Estratégia de tratamento de exceções padrão + +Quando ocorre uma exceção na tarefa em segundo plano, o ABP fornece a **estratégia de tratamento padrão** que tenta novamente a cada 3 segundos, até 3 vezes. Você pode alterar a contagem de tentativas e o intervalo de tentativa por meio das opções `AbpBackgroundJobQuartzOptions`: + +```csharp +[DependsOn( + //...outras dependências + typeof(AbpBackgroundJobsQuartzModule) //Adicione a nova dependência do módulo + )] +public class SeuModulo : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure<AbpBackgroundJobQuartzOptions>(options => + { + options.RetryCount = 1; + options.RetryIntervalMillisecond = 1000; + }); + } +} +``` + +### Personalizar a estratégia de tratamento de exceções + +Você pode personalizar a estratégia de tratamento de exceções por meio das opções `AbpBackgroundJobQuartzOptions`: + +```csharp +[DependsOn( + //...outras dependências + typeof(AbpBackgroundJobsQuartzModule) //Adicione a nova dependência do módulo + )] +public class SeuModulo : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure<AbpBackgroundJobQuartzOptions>(options => + { + options.RetryStrategy = async (retryIndex, executionContext, exception) => + { + // personalizar o tratamento de exceções + }; + }); + } +} +```</source> \ No newline at end of file diff --git a/docs/pt-BR/Background-Workers-Quartz.md b/docs/pt-BR/Background-Workers-Quartz.md new file mode 100644 index 0000000000..857de2e469 --- /dev/null +++ b/docs/pt-BR/Background-Workers-Quartz.md @@ -0,0 +1,146 @@ +# Gerenciador de Trabalhadores em Segundo Plano Quartz + +[Quartz](https://www.quartz-scheduler.net/) é um avançado gerenciador de trabalhadores em segundo plano. Você pode integrar o Quartz com o ABP Framework para usá-lo em vez do [gerenciador de trabalhadores em segundo plano padrão](Background-Workers.md). O ABP simplesmente integra o Quartz. + +## Instalação + +É sugerido usar o [ABP CLI](CLI.md) para instalar este pacote. + +### Usando o ABP CLI + +Abra uma janela de linha de comando na pasta do projeto (arquivo .csproj) e digite o seguinte comando: + +````bash +abp add-package Volo.Abp.BackgroundWorkers.Quartz +```` + +### Instalação Manual + +Se você deseja instalar manualmente: + +1. Adicione o pacote NuGet [Volo.Abp.BackgroundWorkers.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Quartz) ao seu projeto: + + ```` + Install-Package Volo.Abp.BackgroundWorkers.Quartz + ```` + +2. Adicione o módulo `AbpBackgroundWorkersQuartzModule` à lista de dependências do seu módulo: + +````csharp +[DependsOn( + //...outras dependências + typeof(AbpBackgroundWorkersQuartzModule) //Adicione a nova dependência do módulo + )] +public class SeuModulo : AbpModule +{ +} +```` + +> A integração do trabalhador em segundo plano do Quartz fornece o adaptador `QuartzPeriodicBackgroundWorkerAdapter` para adaptar as classes derivadas `PeriodicBackgroundWorkerBase` e `AsyncPeriodicBackgroundWorkerBase`. Portanto, você ainda pode seguir o [documento de trabalhadores em segundo plano](Background-Workers.md) para definir o trabalhador em segundo plano. + +## Configuração + +Veja [Configuração](Background-Jobs-Quartz#Configuração). + +## Criar um Trabalhador em Segundo Plano + +Um trabalho em segundo plano é uma classe que deriva da classe base `QuartzBackgroundWorkerBase`. Por exemplo, uma classe de trabalhador simples é mostrada abaixo: + +```` csharp +public class MeuTrabalhadorDeLog : QuartzBackgroundWorkerBase +{ + public MeuTrabalhadorDeLog() + { + JobDetail = JobBuilder.Create<MeuTrabalhadorDeLog>().WithIdentity(nameof(MeuTrabalhadorDeLog)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MeuTrabalhadorDeLog)).StartNow().Build(); + } + + public override Task Execute(IJobExecutionContext context) + { + Logger.LogInformation("Executando MeuTrabalhadorDeLog..!"); + return Task.CompletedTask; + } +} +```` + +Nós simplesmente implementamos o método Execute para escrever um log. O trabalhador em segundo plano é um **singleton por padrão**. Se desejar, você também pode implementar uma [interface de dependência](Dependency-Injection#Interfaces-de-Dependência) para registrá-lo com outro ciclo de vida. + +> Dica: Adicionar identidade aos trabalhadores em segundo plano é uma boa prática, pois o Quartz distingue trabalhos diferentes com base na identidade. + +## Adicionar ao BackgroundWorkerManager + +Os trabalhadores em segundo plano padrão são **adicionados automaticamente** ao BackgroundWorkerManager quando a aplicação é **inicializada**. Você pode definir o valor da propriedade `AutoRegister` como `false`, se desejar adicioná-lo manualmente: + +```` csharp +public class MeuTrabalhadorDeLog : QuartzBackgroundWorkerBase +{ + public MeuTrabalhadorDeLog() + { + AutoRegister = false; + JobDetail = JobBuilder.Create<MeuTrabalhadorDeLog>().WithIdentity(nameof(MeuTrabalhadorDeLog)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MeuTrabalhadorDeLog)).StartNow().Build(); + } + + public override Task Execute(IJobExecutionContext context) + { + Logger.LogInformation("Executando MeuTrabalhadorDeLog..!"); + return Task.CompletedTask; + } +} +```` + +Se você deseja desabilitar globalmente a adição automática de trabalhadores, você pode desabilitá-la globalmente através das opções `AbpBackgroundWorkerQuartzOptions`: + +```csharp +[DependsOn( + //...outras dependências + typeof(AbpBackgroundWorkersQuartzModule) //Adicione a nova dependência do módulo + )] +public class SeuModulo : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure<AbpBackgroundWorkerQuartzOptions>(options => + { + options.IsAutoRegisterEnabled = false; + }); + } +} +``` + +## Tópicos Avançados + +### Personalizar o ScheduleJob + +Suponha que você tenha um trabalhador que é executado a cada 10 minutos, mas devido a um servidor indisponível por 30 minutos, 3 execuções são perdidas. Você deseja executar todas as execuções perdidas quando o servidor estiver disponível novamente. Você deve definir seu trabalhador em segundo plano da seguinte forma: + +```csharp +public class MeuTrabalhadorDeLog : QuartzBackgroundWorkerBase +{ + public MeuTrabalhadorDeLog() + { + JobDetail = JobBuilder.Create<MeuTrabalhadorDeLog>().WithIdentity(nameof(MeuTrabalhadorDeLog)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MeuTrabalhadorDeLog)).WithSimpleSchedule(s=>s.WithIntervalInMinutes(1).RepeatForever().WithMisfireHandlingInstructionIgnoreMisfires()).Build(); + + ScheduleJob = async scheduler => + { + if (!await scheduler.CheckExists(JobDetail.Key)) + { + await scheduler.ScheduleJob(JobDetail, Trigger); + } + }; + } + + public override Task Execute(IJobExecutionContext context) + { + Logger.LogInformation("Executando MeuTrabalhadorDeLog..!"); + return Task.CompletedTask; + } +} +``` + +No exemplo, definimos o intervalo de execução do trabalhador como 10 minutos e definimos `WithMisfireHandlingInstructionIgnoreMisfires`. Personalizamos o `ScheduleJob` e adicionamos o trabalhador ao Quartz somente quando o trabalhador em segundo plano não existe. + +### Mais + +Consulte a [documentação](https://www.quartz-scheduler.net/documentation/index.html) do Quartz para obter mais informações. diff --git a/docs/pt-BR/Background-Workers.md b/docs/pt-BR/Background-Workers.md new file mode 100644 index 0000000000..7ee7f3afd2 --- /dev/null +++ b/docs/pt-BR/Background-Workers.md @@ -0,0 +1,144 @@ +# Background Workers + +## Introdução + +Background workers são threads independentes simples na aplicação que são executadas em segundo plano. Geralmente, eles são executados periodicamente para realizar algumas tarefas. Exemplos: + +* Um background worker pode ser executado periodicamente para **excluir logs antigos**. +* Um background worker pode ser executado periodicamente para **identificar usuários inativos** e **enviar e-mails** para fazer com que os usuários retornem à sua aplicação. + +## Criando um Background Worker + +Um background worker deve implementar diretamente ou indiretamente a interface `IBackgroundWorker`. + +> Um background worker é inerentemente [singleton](Dependency-Injection.md). Portanto, apenas uma única instância da sua classe de worker é instanciada e executada. + +### BackgroundWorkerBase + +`BackgroundWorkerBase` é uma maneira fácil de criar um background worker. + +````csharp +public class MyWorker : BackgroundWorkerBase +{ + public override Task StartAsync(CancellationToken cancellationToken = default) + { + //... + } + + public override Task StopAsync(CancellationToken cancellationToken = default) + { + //... + } +} +```` + +Inicie o seu worker no método `StartAsync` (que é chamado quando a aplicação é iniciada) e pare no método `StopAsync` (que é chamado quando a aplicação é encerrada). + +> Você pode implementar diretamente a interface `IBackgroundWorker`, mas `BackgroundWorkerBase` fornece algumas propriedades úteis, como `Logger`. + +### AsyncPeriodicBackgroundWorkerBase + +Vamos supor que queremos tornar um usuário inativo se ele não tiver feito login na aplicação nos últimos 30 dias. A classe `AsyncPeriodicBackgroundWorkerBase` simplifica a criação de workers periódicos, então vamos usá-la no exemplo abaixo: + +````csharp +public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase +{ + public PassiveUserCheckerWorker( + AbpAsyncTimer timer, + IServiceScopeFactory serviceScopeFactory + ) : base( + timer, + serviceScopeFactory) + { + Timer.Period = 600000; //10 minutos + } + + protected async override Task DoWorkAsync( + PeriodicBackgroundWorkerContext workerContext) + { + Logger.LogInformation("Iniciando: Definindo status de usuários inativos..."); + + //Resolver dependências + var userRepository = workerContext + .ServiceProvider + .GetRequiredService<IUserRepository>(); + + //Realizar o trabalho + await userRepository.UpdateInactiveUserStatusesAsync(); + + Logger.LogInformation("Concluído: Definindo status de usuários inativos..."); + } +} +```` + +* `AsyncPeriodicBackgroundWorkerBase` usa o objeto `AbpAsyncTimer` (um timer thread-safe) para determinar **o período**. Podemos definir a propriedade `Period` no construtor. +* É necessário implementar o método `DoWorkAsync` para **executar** o trabalho periódico. +* É uma boa prática **resolver as dependências** a partir do `PeriodicBackgroundWorkerContext` em vez de usar injeção de dependência no construtor. Isso ocorre porque `AsyncPeriodicBackgroundWorkerBase` usa um `IServiceScope` que é **descartado** quando o trabalho é concluído. +* `AsyncPeriodicBackgroundWorkerBase` **captura e registra exceções** lançadas pelo método `DoWorkAsync`. + +## Registrando o Background Worker + +Após criar a classe do background worker, você deve adicioná-la ao `IBackgroundWorkerManager`. O local mais comum é o método `OnApplicationInitializationAsync` da sua classe de módulo: + +````csharp +[DependsOn(typeof(AbpBackgroundWorkersModule))] +public class MyModule : AbpModule +{ + public override async Task OnApplicationInitializationAsync( + ApplicationInitializationContext context) + { + await context.AddBackgroundWorkerAsync<PassiveUserCheckerWorker>(); + } +} +```` + +`context.AddBackgroundWorkerAsync(...)` é um método de extensão que simplifica a expressão abaixo: + +````csharp +await context.ServiceProvider + .GetRequiredService<IBackgroundWorkerManager>() + .AddAsync( + context + .ServiceProvider + .GetRequiredService<PassiveUserCheckerWorker>() + ); +```` + +Dessa forma, ele resolve o background worker fornecido e o adiciona ao `IBackgroundWorkerManager`. + +Embora geralmente adicionemos workers no método `OnApplicationInitializationAsync`, não há restrições quanto a isso. Você pode injetar o `IBackgroundWorkerManager` em qualquer lugar e adicionar workers em tempo de execução. O gerenciador de background workers irá parar e liberar todos os workers registrados quando a aplicação for encerrada. + +## Opções + +A classe `AbpBackgroundWorkerOptions` é usada para [definir opções](Options.md) para os background workers. Atualmente, há apenas uma opção: + +* `IsEnabled` (padrão: true): Usado para **habilitar/desabilitar** o sistema de background workers para a sua aplicação. + +> Consulte o documento [Options](Options.md) para aprender como definir opções. + +## Mantendo a Aplicação Sempre em Execução + +Os background workers só funcionam se a sua aplicação estiver em execução. Se você hospedar a execução do job em segundo plano na sua aplicação web (esse é o comportamento padrão), você deve garantir que a sua aplicação web esteja configurada para sempre estar em execução. Caso contrário, os jobs em segundo plano só funcionarão enquanto a sua aplicação estiver em uso. + +## Executando em um Cluster + +Tenha cuidado se você executar várias instâncias da sua aplicação simultaneamente em um ambiente de cluster. Nesse caso, cada aplicação executa o mesmo worker, o que pode criar conflitos se os workers estiverem executando em recursos compartilhados (processando os mesmos dados, por exemplo). + +Se isso for um problema para os seus workers, você tem as seguintes opções: + +* Implemente os seus background workers de forma que eles funcionem em um ambiente de cluster sem problemas. Usar o [lock distribuído](Distributed-Locking.md) para garantir o controle de concorrência é uma forma de fazer isso. Um background worker em uma instância da aplicação pode manipular um lock distribuído, então os workers em outras instâncias da aplicação aguardarão pelo lock. Dessa forma, apenas um worker realiza o trabalho real, enquanto os outros esperam ociosos. Se você implementar isso, os seus workers serão executados com segurança, independentemente de como a aplicação estiver implantada. +* Pare os background workers (defina `AbpBackgroundWorkerOptions.IsEnabled` como `false`) em todas as instâncias da aplicação, exceto em uma delas, para que apenas a instância única execute os workers. +* Pare os background workers (defina `AbpBackgroundWorkerOptions.IsEnabled` como `false`) em todas as instâncias da aplicação e crie uma aplicação dedicada (talvez uma aplicação console executando em seu próprio contêiner ou um serviço do Windows em execução em segundo plano) para executar todas as tarefas em segundo plano. Essa pode ser uma boa opção se os seus background workers consumirem muitos recursos do sistema (CPU, RAM ou Disco), para que você possa implantar essa aplicação de background em um servidor dedicado e suas tarefas em segundo plano não afetem o desempenho da sua aplicação. + +## Integrações + +O sistema de background workers é extensível e você pode alterar o gerenciador de background workers padrão com a sua própria implementação ou com uma das integrações pré-construídas. + +Veja as alternativas de gerenciador de workers pré-construídas: + +* [Quartz Background Worker Manager](Background-Workers-Quartz.md) +* [Hangfire Background Worker Manager](Background-Workers-Hangfire.md) + +## Veja Também + +* [Background Jobs](Background-Jobs.md) diff --git a/docs/pt-BR/Exception-Handling.md b/docs/pt-BR/Exception-Handling.md new file mode 100644 index 0000000000..288badbdff --- /dev/null +++ b/docs/pt-BR/Exception-Handling.md @@ -0,0 +1,343 @@ +# Tratamento de Exceções + +O ABP fornece uma infraestrutura integrada e oferece um modelo padrão para lidar com exceções. + +* **Lida automaticamente com todas as exceções** e envia uma **mensagem de erro formatada padrão** para o cliente em uma solicitação de API/AJAX. +* Oculta automaticamente os **erros internos de infraestrutura** e retorna uma mensagem de erro padrão. +* Fornece uma maneira fácil e configurável de **localizar** mensagens de exceção. +* Mapeia automaticamente exceções padrão para **códigos de status HTTP** e fornece uma opção configurável para mapear exceções personalizadas. + +## Tratamento Automático de Exceções + +O `AbpExceptionFilter` lida com uma exceção se **qualquer uma das seguintes condições** forem atendidas: + +* A exceção é lançada por uma **ação do controlador** que retorna um **resultado de objeto** (não um resultado de visualização). +* A solicitação é uma solicitação AJAX (o valor do cabeçalho HTTP `X-Requested-With` é `XMLHttpRequest`). +* O cliente aceita explicitamente o tipo de conteúdo `application/json` (por meio do cabeçalho HTTP `accept`). + +Se a exceção for tratada, ela é automaticamente **registrada** e uma **mensagem JSON formatada** é retornada ao cliente. + +### Formato da Mensagem de Erro + +A mensagem de erro é uma instância da classe `RemoteServiceErrorResponse`. O JSON de erro mais simples tem uma propriedade **message** conforme mostrado abaixo: + +````json +{ + "error": { + "message": "Este tópico está bloqueado e não é possível adicionar uma nova mensagem" + } +} +```` + +Existem **campos opcionais** que podem ser preenchidos com base na exceção que ocorreu. + +##### Código de Erro + +O **código de erro** é um valor de string opcional e único para a exceção. A exceção lançada deve implementar a interface `IHasErrorCode` para preencher este campo. Exemplo de valor JSON: + +````json +{ + "error": { + "code": "App:010042", + "message": "Este tópico está bloqueado e não é possível adicionar uma nova mensagem" + } +} +```` + +O código de erro também pode ser usado para localizar a exceção e personalizar o código de status HTTP (consulte as seções relacionadas abaixo). + +##### Detalhes do Erro + +Os **detalhes do erro** são um campo opcional da mensagem de erro JSON. A exceção lançada deve implementar a interface `IHasErrorDetails` para preencher este campo. Exemplo de valor JSON: + +```json +{ + "error": { + "code": "App:010042", + "message": "Este tópico está bloqueado e não é possível adicionar uma nova mensagem", + "details": "Informações mais detalhadas sobre o erro..." + } +} +``` + +##### Erros de Validação + +**validationErrors** é um campo padrão que é preenchido se a exceção lançada implementar a interface `IHasValidationErrors`. + +````json +{ + "error": { + "code": "App:010046", + "message": "Sua solicitação não é válida, corrija e tente novamente!", + "validationErrors": [{ + "message": "O nome de usuário deve ter no mínimo 3 caracteres.", + "members": ["userName"] + }, + { + "message": "A senha é obrigatória", + "members": ["password"] + }] + } +} +```` + +`AbpValidationException` implementa a interface `IHasValidationErrors` e é automaticamente lançada pelo framework quando a entrada de uma solicitação não é válida. Portanto, geralmente você não precisa lidar com erros de validação, a menos que tenha lógica de validação altamente personalizada. + +### Registro + +As exceções capturadas são automaticamente registradas. + +#### Nível de Registro + +As exceções são registradas com o nível `Error` por padrão. O nível de log pode ser determinado pela exceção se ela implementar a interface `IHasLogLevel`. Exemplo: + +````C# +public class MinhaExcecao : Exception, IHasLogLevel +{ + public LogLevel LogLevel { get; set; } = LogLevel.Warning; + + //... +} +```` + +#### Exceções de Registro Próprio + +Alguns tipos de exceção podem precisar escrever logs adicionais. Eles podem implementar a interface `IExceptionWithSelfLogging` se necessário. Exemplo: + +````C# +public class MinhaExcecao : Exception, IExceptionWithSelfLogging +{ + public void Log(ILogger logger) + { + //...log informações adicionais + } +} +```` + +> Os métodos de extensão `ILogger.LogException` são usados para escrever logs de exceção. Você pode usar o mesmo método de extensão quando necessário. + +## Exceções de Negócios + +A maioria de suas próprias exceções será exceções de negócios. A interface `IBusinessException` é usada para marcar uma exceção como uma exceção de negócios. + +`BusinessException` implementa a interface `IBusinessException` além das interfaces `IHasErrorCode`, `IHasErrorDetails` e `IHasLogLevel`. O nível de log padrão é `Warning`. + +Normalmente, você tem um código de erro relacionado a uma exceção de negócios específica. Por exemplo: + +````C# +throw new BusinessException(QaErrorCodes.CanNotVoteYourOwnAnswer); +```` + +`QaErrorCodes.CanNotVoteYourOwnAnswer` é apenas uma `const string`. O seguinte formato de código de erro é recomendado: + +```` +<namespace-do-código>:<código-de-erro> +```` + +**namespace-do-código** é um **valor único** específico para o seu módulo/aplicação. Exemplo: + +```` +Volo.Qa:010002 +```` + +`Volo.Qa` é o namespace do código aqui. O namespace do código será então usado ao **localizar** mensagens de exceção. + +* Você pode **lançar diretamente** uma `BusinessException` ou **derivar** seus próprios tipos de exceção dela quando necessário. +* Todas as propriedades são opcionais para a classe `BusinessException`. Mas geralmente você define a propriedade `ErrorCode` ou `Message`. + +## Localização de Exceções + +Um problema ao lançar exceções é como localizar mensagens de erro ao enviá-las para o cliente. O ABP oferece dois modelos e suas variantes. + +### Exceção Amigável ao Usuário + +Se uma exceção implementa a interface `IUserFriendlyException`, então o ABP não altera suas propriedades `Message` e `Details` e a envia diretamente para o cliente. + +A classe `UserFriendlyException` é a implementação integrada da interface `IUserFriendlyException`. Exemplo de uso: + +````C# +throw new UserFriendlyException( + "O nome de usuário deve ser único!" +); +```` + +Dessa forma, **não há necessidade de localização**. Se você deseja localizar a mensagem, pode injetar e usar o **localizador de strings padrão** (consulte o [documento de localização](Localization.md)). Exemplo: + +````C# +throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage"]); +```` + +Em seguida, defina no **recurso de localização** para cada idioma. Exemplo: + +````json +{ + "culture": "pt", + "texts": { + "UserNameShouldBeUniqueMessage": "O nome de usuário deve ser único!" + } +} +```` + +O localizador de strings já suporta **mensagens parametrizadas**. Por exemplo: + +````C# +throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage", "john"]); +```` + +Em seguida, o texto de localização pode ser: + +````json +"UserNameShouldBeUniqueMessage": "O nome de usuário deve ser único! '{0}' já está em uso!" +```` + +* A interface `IUserFriendlyException` é derivada da `IBusinessException` e a classe `UserFriendlyException` é derivada da classe `BusinessException`. + +### Usando Códigos de Erro + +`UserFriendlyException` é bom, mas tem alguns problemas em usos avançados: + +* Requer que você **injete o localizador de strings** em todos os lugares e sempre o use ao lançar exceções. +* No entanto, em alguns casos, pode **não ser possível** injetar o localizador de strings (em um contexto estático ou em um método de entidade). + +Em vez de localizar a mensagem ao lançar a exceção, você pode separar o processo usando **códigos de erro**. + +Primeiro, defina o mapeamento do **namespace-do-código** para o **recurso de localização** na configuração do módulo: + +````C# +services.Configure<AbpExceptionLocalizationOptions>(options => +{ + options.MapCodeNamespace("Volo.Qa", typeof(QaResource)); +}); +```` + +Então, qualquer uma das exceções com o namespace `Volo.Qa` será localizada usando seu recurso de localização fornecido. O recurso de localização deve sempre ter uma entrada com a chave do código de erro. Exemplo: + +````json +{ + "culture": "pt", + "texts": { + "Volo.Qa:010002": "Você não pode votar em sua própria resposta!" + } +} +```` + +Então uma exceção de negócios pode ser lançada com o código de erro: + +````C# +throw new BusinessException(QaDomainErrorCodes.CanNotVoteYourOwnAnswer); +```` + +* Lançar qualquer exceção que implemente a interface `IHasErrorCode` se comporta da mesma maneira. Portanto, a abordagem de localização de código de erro não é exclusiva para a classe `BusinessException`. +* Não é necessário definir uma string localizada para uma mensagem de erro. Se não estiver definido, o ABP envia a mensagem de erro padrão para o cliente. Ele não usa a propriedade `Message` da exceção! se você deseja isso, use a `UserFriendlyException` (ou use um tipo de exceção que implemente a interface `IUserFriendlyException`). + +#### Usando Parâmetros de Mensagem + +Se você tiver uma mensagem de erro parametrizada, poderá defini-la com a propriedade `Data` da exceção. Por exemplo: + +````C# +throw new BusinessException("App:010046") +{ + Data = + { + {"UserName", "john"} + } +}; + +```` + +Felizmente, há uma maneira mais simples de codificar isso: + +````C# +throw new BusinessException("App:010046") + .WithData("UserName", "john"); +```` + +Em seguida, o texto localizado pode conter o parâmetro `UserName`: + +````json +{ + "culture": "pt", + "texts": { + "App:010046": "O nome de usuário deve ser único. '{UserName}' já está em uso!" + } +} +```` + +* `WithData` pode ser encadeado com mais de um parâmetro (como `.WithData(...).WithData(...)`). + +## Mapeamento de Códigos de Status HTTP + +O ABP tenta determinar automaticamente o código de status HTTP mais adequado para tipos comuns de exceção seguindo estas regras: + +* Para a `AbpAuthorizationException`: + * Retorna `401` (não autorizado) se o usuário não estiver logado. + * Retorna `403` (proibido) se o usuário estiver logado. +* Retorna `400` (requisição inválida) para a `AbpValidationException`. +* Retorna `404` (não encontrado) para a `EntityNotFoundException`. +* Retorna `403` (proibido) para a `IBusinessException` (e `IUserFriendlyException` já que estende a `IBusinessException`). +* Retorna `501` (não implementado) para a `NotImplementedException`. +* Retorna `500` (erro interno do servidor) para outras exceções (que são assumidas como exceções de infraestrutura). + +O `IHttpExceptionStatusCodeFinder` é usado para determinar automaticamente o código de status HTTP. A implementação padrão é a classe `DefaultHttpExceptionStatusCodeFinder`. Pode ser substituída ou estendida conforme necessário. + +### Mapeamentos Personalizados + +A determinação automática do código de status HTTP pode ser substituída por mapeamentos personalizados. Por exemplo: + +````C# +services.Configure<AbpExceptionHttpStatusCodeOptions>(options => +{ + options.Map("Volo.Qa:010002", HttpStatusCode.Conflict); +}); +```` + +## Inscrevendo-se nas Exceções + +É possível ser informado quando o Framework ABP **manipula uma exceção**. Ele registra automaticamente todas as exceções no [logger padrão](Logging.md), mas você pode querer fazer mais. + +Nesse caso, crie uma classe derivada da classe `ExceptionSubscriber` em sua aplicação: + +````csharp +public class MeuAssinanteDeExcecao : ExceptionSubscriber +{ + public async override Task HandleAsync(ExceptionNotificationContext context) + { + //TODO... + } +} +```` + +O objeto `context` contém informações necessárias sobre a exceção ocorrida. + +> Você pode ter vários assinantes, cada um recebe uma cópia da exceção. As exceções lançadas pelo seu assinante são ignoradas (mas ainda registradas). + +## Exceções Integradas + +Alguns tipos de exceção são automaticamente lançados pelo framework: + +- `AbpAuthorizationException` é lançada se o usuário atual não tiver permissão para realizar a operação solicitada. Consulte [autorização](Authorization.md) para mais informações. +- `AbpValidationException` é lançada se a entrada da solicitação atual não for válida. Consulte [validação](Validation.md) para mais informações. +- `EntityNotFoundException` é lançada se a entidade solicitada não estiver disponível. Isso é lançado principalmente por [repositórios](Repositories.md). + +Você também pode lançar esses tipos de exceção em seu código (embora raramente seja necessário). + +## AbpExceptionHandlingOptions + +`AbpExceptionHandlingOptions` é o principal [objeto de opções](Options.md) para configurar o sistema de tratamento de exceções. Você pode configurá-lo no método `ConfigureServices` do seu [módulo](Module-Development-Basics.md): + +````csharp +Configure<AbpExceptionHandlingOptions>(options => +{ + options.SendExceptionsDetailsToClients = true; + options.SendStackTraceToClients = false; +}); +```` + +Aqui está uma lista das opções que você pode configurar: + +* `SendExceptionsDetailsToClients` (padrão: `false`): Você pode habilitar ou desabilitar o envio de detalhes da exceção para o cliente. +* `SendStackTraceToClients` (padrão: `true`): Você pode habilitar ou desabilitar o envio da pilha de chamadas da exceção para o cliente. Se você deseja enviar a pilha de chamadas para o cliente, deve definir tanto as opções `SendStackTraceToClients` quanto `SendExceptionsDetailsToClients` como `true`, caso contrário, a pilha de chamadas não será enviada para o cliente. + +## Veja Também + +* [Tutorial em vídeo](https://abp.io/video-courses/essentials/exception-handling) diff --git a/docs/pt-BR/Modules/Account.md b/docs/pt-BR/Modules/Account.md new file mode 100644 index 0000000000..af1f4962db --- /dev/null +++ b/docs/pt-BR/Modules/Account.md @@ -0,0 +1,80 @@ +# Módulo de Conta + +O módulo de conta implementa recursos básicos de autenticação, como **login**, **registro**, **recuperação de senha** e **gerenciamento de conta**. + +Este módulo é baseado na biblioteca de identidade da Microsoft e no módulo de identidade. Ele possui integração com o IdentityServer (com base no módulo IdentityServer) e com o OpenIddict (com base no módulo OpenIddict) para fornecer autenticação avançada, controle de acesso e outros recursos avançados de autenticação. + +## Como instalar + +Este módulo já vem pré-instalado (como pacotes NuGet/NPM). Você pode continuar a usá-lo como pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` da CLI) para desenvolver seu próprio módulo personalizado. + +### O código-fonte + +O código-fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/account). O código-fonte está licenciado sob a licença MIT, portanto, você pode usá-lo e personalizá-lo livremente. + +## Interface do usuário + +Esta seção apresenta as principais páginas fornecidas por este módulo. + +### Login + +A página `/Account/Login` fornece a funcionalidade de login. + +![account-module-login](../images/account-module-login.png) + +Os botões de login social/externo ficam visíveis se você configurá-los. Consulte a seção *Logins Sociais/Externos* abaixo. Os links de registro e recuperação de senha redirecionam para as páginas explicadas nas próximas seções. + +### Registro + +A página `/Account/Register` fornece a funcionalidade de registro de novo usuário. + +![account-module-register](../images/account-module-register.png) + +### Recuperação de senha e redefinição de senha + +A página `/Account/ForgotPassword` fornece uma maneira de enviar um link de redefinição de senha para o endereço de e-mail do usuário. O usuário então clica no link e define uma nova senha. + +![account-module-forgot-password](../images/account-module-forgot-password.png) + +### Gerenciamento de conta + +A página `/Account/Manage` é usada para alterar a senha e as informações pessoais do usuário. + +![account-module-manage-account](../images/account-module-manage-account.png) + +## Integração com o OpenIddict + +O pacote [Volo.Abp.Account.Web.OpenIddict](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) fornece integração com o OpenIddict. Este pacote já vem instalado com o modelo de inicialização do aplicativo. Consulte a documentação do módulo OpenIddict. + +## Integração com o IdentityServer + +O pacote [Volo.Abp.Account.Web.IdentityServer](https://www.nuget.org/packages/Volo.Abp.Account.Web.IdentityServer) fornece integração com o IdentityServer. Este pacote já vem instalado com o modelo de inicialização do aplicativo. Consulte a documentação do módulo IdentityServer. + +## Logins Sociais/Externos + +O módulo de conta já está configurado para lidar com logins sociais ou externos prontamente. Você pode seguir a documentação do ASP.NET Core para adicionar um provedor de login social/externo à sua aplicação. + +### Exemplo: Autenticação do Facebook + +Siga o documento de integração do Facebook do ASP.NET Core para oferecer suporte ao login do Facebook em sua aplicação. + +#### Adicionar o pacote NuGet + +Adicione o pacote [Microsoft.AspNetCore.Authentication.Facebook](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.Facebook) ao seu projeto. Com base na sua arquitetura, isso pode ser feito no projeto `.Web`, `.IdentityServer` (para configuração em camadas) ou `.Host`. + +#### Configurar o provedor + +Use o método de extensão `.AddFacebook(...)` no método `ConfigureServices` do seu [módulo](../Module-Development-Basics.md) para configurar o cliente: + +````csharp +context.Services.AddAuthentication() + .AddFacebook(facebook => + { + facebook.AppId = "..."; + facebook.AppSecret = "..."; + facebook.Scope.Add("email"); + facebook.Scope.Add("public_profile"); + }); +```` + +> Seria uma prática melhor usar o `appsettings.json` ou o sistema de segredos do usuário do ASP.NET Core para armazenar suas credenciais, em vez de um valor codificado como esse. Siga o documento da Microsoft para aprender a usar os segredos do usuário. \ No newline at end of file diff --git a/docs/pt-BR/Modules/Audit-Logging.md b/docs/pt-BR/Modules/Audit-Logging.md new file mode 100644 index 0000000000..4f7aedf82d --- /dev/null +++ b/docs/pt-BR/Modules/Audit-Logging.md @@ -0,0 +1,60 @@ +# Módulo de Registro de Auditoria + +O Módulo de Registro de Auditoria basicamente implementa o `IAuditingStore` para salvar os objetos de registro de auditoria em um banco de dados. + +> Este documento abrange apenas o módulo de registro de auditoria que persiste os registros de auditoria em um banco de dados. Consulte o documento [registro de auditoria](../Audit-Logging.md) para obter mais informações sobre o sistema de registro de auditoria. + +## Como Instalar + +Este módulo vem pré-instalado (como pacotes NuGet/NPM). Você pode continuar a usá-lo como pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` [CLI](../CLI.md)) para desenvolver seu módulo personalizado. + +### O Código Fonte + +O código fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/audit-logging). O código fonte é licenciado com [MIT](https://choosealicense.com/licenses/mit/), então você pode usá-lo e personalizá-lo livremente. + +## Internos + +### Camada de Domínio + +#### Agregados + +- `AuditLog` (raiz do agregado): Representa um registro de log de auditoria no sistema. + - `EntityChange` (coleção): Entidades alteradas do registro de auditoria. + - `AuditLogAction` (coleção): Ações executadas do registro de auditoria. + +#### Repositórios + +Os seguintes repositórios personalizados são definidos para este módulo: + +- `IAuditLogRepository` + +### Provedores de Banco de Dados + +#### Comum + +##### Prefixo da tabela / coleção e esquema + +Todas as tabelas/coleções usam o prefixo `Abp` por padrão. Defina propriedades estáticas na classe `AbpAuditLoggingDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de conexão + +Este módulo usa `AbpAuditLogging` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será usada a string de conexão `Default`. Consulte a documentação sobre [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter mais detalhes. + +#### Entity Framework Core + +##### Tabelas + +- **AbpAuditLogs** + - AbpAuditLogActions + - AbpEntityChanges + - AbpEntityPropertyChanges + +#### MongoDB + +##### Coleções + +- **AbpAuditLogs** + +## Veja Também + +* [Sistema de registro de auditoria](../Audit-Logging.md) \ No newline at end of file diff --git a/docs/pt-BR/Modules/Background-Jobs.md b/docs/pt-BR/Modules/Background-Jobs.md new file mode 100644 index 0000000000..137ea84d7e --- /dev/null +++ b/docs/pt-BR/Modules/Background-Jobs.md @@ -0,0 +1,55 @@ +# Módulo de Tarefas em Segundo Plano + +O módulo de Tarefas em Segundo Plano implementa a interface `IBackgroundJobStore` e torna possível usar o gerenciador de tarefas em segundo plano padrão do ABP Framework. Se você não deseja usar este módulo, então você deve implementar a interface `IBackgroundJobStore` por conta própria. + +> Este documento aborda apenas o módulo de tarefas em segundo plano que persiste as tarefas em segundo plano em um banco de dados. Consulte o documento [tarefas em segundo plano](../Background-Jobs.md) para obter mais informações sobre o sistema de tarefas em segundo plano. + +## Como Instalar + +Este módulo vem pré-instalado (como pacotes NuGet/NPM). Você pode continuar a usá-lo como pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` [CLI](../CLI.md)) para desenvolver seu próprio módulo personalizado. + +### O Código Fonte + +O código-fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/background-jobs). O código-fonte é licenciado com [MIT](https://choosealicense.com/licenses/mit/), então você pode usá-lo e personalizá-lo livremente. + +## Internos + +### Camada de Domínio + +#### Agregados + +- `BackgroundJobRecord` (raiz do agregado): Representa um registro de tarefa em segundo plano. + +#### Repositórios + +Os seguintes repositórios personalizados são definidos para este módulo: + +- `IBackgroundJobRepository` + +### Provedores de Banco de Dados + +#### Comum + +##### Prefixo da tabela / coleção e esquema + +Todas as tabelas/coleções usam o prefixo `Abp` por padrão. Defina propriedades estáticas na classe `BackgroundJobsDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de conexão + +Este módulo usa `AbpBackgroundJobs` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será usada a string de conexão `Default`. Consulte a documentação sobre [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter mais detalhes. + +#### Entity Framework Core + +##### Tabelas + +- **AbpBackgroundJobs** + +#### MongoDB + +##### Coleções + +- **AbpBackgroundJobs** + +## Veja Também + +* [Sistema de tarefas em segundo plano](../Background-Jobs.md) \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Blogging.md b/docs/pt-BR/Modules/Cms-Kit/Blogging.md new file mode 100644 index 0000000000..075222e1d9 --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Blogging.md @@ -0,0 +1,135 @@ +# Kit CMS: Blogging + +A funcionalidade de blogging fornece a interface necessária para gerenciar e renderizar blogs e postagens de blog. + +## Habilitando a funcionalidade de blogging + +Por padrão, as funcionalidades do Kit CMS estão desabilitadas. Portanto, você precisa habilitar as funcionalidades que deseja antes de começar a usá-lo. Você pode usar o sistema de [Global Feature](../../Global-Features.md) para habilitar/desabilitar as funcionalidades do Kit CMS durante o desenvolvimento. Alternativamente, você pode usar o [Sistema de Funcionalidades](https://docs.abp.io/en/abp/latest/Features) do ABP Framework para desabilitar uma funcionalidade do Kit CMS em tempo de execução. + +> Verifique a seção "Como instalar" da documentação do módulo Kit CMS (Index.md#how-to-install) para saber como habilitar/desabilitar as funcionalidades do Kit CMS durante o desenvolvimento. + +## Interface do usuário + +### Itens do menu + +Os seguintes itens de menu são adicionados pela funcionalidade de blogging à aplicação de administração: + +* **Blogs**: Página de gerenciamento de blogs. +* **Postagens de blog**: Página de gerenciamento de postagens de blog. + +## Páginas + +### Blogs + +A página de blogs é usada para criar e gerenciar blogs no seu sistema. + +![página-de-blogs](../../images/cmskit-module-blogs-page.png) + +Uma captura de tela do modal de criação de novo blog: + +![edição-de-blogs](../../images/cmskit-module-blogs-edit.png) + +**Slug** é a parte da URL do blog. Para este exemplo, a URL raiz do blog se torna `seu-domínio.com/blogs/blog-técnico/`. + +- Você pode alterar o slug padrão usando a constante `CmsBlogsWebConsts.BlogRoutePrefix`. Por exemplo, se você definir como `foo`, a URL raiz do blog se torna `seu-domínio.com/foo/blog-técnico/`. + + ```csharp + public override void PreConfigureServices(ServiceConfigurationContext context) + { + CmsBlogsWebConsts.BlogsRoutePrefix = "foo"; + } + ``` + +#### Funcionalidades do blog + +A funcionalidade de blog usa algumas das outras funcionalidades do Kit CMS. Você pode habilitar ou desabilitar as funcionalidades clicando na ação de funcionalidades para um blog. + +![ação-de-funcionalidades-de-blogs](../../images/cmskit-module-blogs-feature-action.png) + +Você pode selecionar/deselecionar as funcionalidades desejadas para as postagens de blog. + +![diálogo-de-funcionalidades](../../images/cmskit-module-features-dialog-2.png) + +##### Barra de navegação rápida na postagem de blog + +Se você habilitar "Barra de navegação rápida nas postagens de blog", será habilitado o índice de rolagem conforme mostrado abaixo. + +![índice-de-rolagem](../../images/cmskit-module-features-scroll-index.png) + +### Gerenciamento de postagens de blog + +Ao criar blogs, você pode gerenciar as postagens de blog nesta página. + +![página-de-postagens-de-blog](../../images/cmskit-module-blog-posts-page.png) + +Você pode criar e editar uma postagem de blog existente nesta página. Se você habilitar funcionalidades específicas, como tags, poderá definir tags para a postagem de blog nesta página. + +![edição-de-postagem-de-blog](../../images/cmskit-module-blog-post-edit.png) + +## Internos + +### Camada de domínio + +#### Agregados + +Este módulo segue o guia de [Melhores Práticas e Convenções de Entidades](https://docs.abp.io/en/abp/latest/Best-Practices/Entities). + +- `Blog` _(raiz do agregado)_: Apresenta blogs da aplicação. +- `BlogPost` _(raiz do agregado)_: Apresenta postagens de blog nos blogs. +- `BlogFeature` _(raiz do agregado)_: Apresenta o estado de habilitação/desabilitação das funcionalidades do blog, como reações, avaliações, comentários, etc. + +#### Repositórios + +Este módulo segue o guia de [Melhores Práticas e Convenções de Repositórios](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories). Os seguintes repositórios são definidos para esta funcionalidade: + +- `IBlogRepository` +- `IBlogPostRepository` +- `IBlogFeatureRepository` + +#### Serviços de domínio + +Este módulo segue o guia de [Melhores Práticas e Convenções de Serviços de Domínio](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services). + +- `BlogManager` +- `BlogPostManager` +- `BlogFeatureManager` + +### Camada de aplicação + +#### Serviços de aplicação + +##### Comum + +- `BlogFeatureAppService` _(implementa `IBlogFeatureAppService`)_ + +##### Administração + +- `BlogAdminAppService` _(implementa `IBlogAdminAppService`)_ +- `BlogFeatureAdminAppService` _(implementa `IBlogFeatureAdminAppService`)_ +- `BlogPostAdminAppService` _(implementa `IBlogPostAdminAppService`)_ + +##### Público + +- `BlogPostPublicAppService` _(implementa `IBlogPostPublicAppService`)_ + +### Provedores de banco de dados + +#### Entity Framework Core + +##### Tabelas + +- CmsBlogs +- CmsBlogPosts +- CmsBlogFeatures + +#### MongoDB + +##### Coleções + +- CmsBlogs +- CmsBlogPosts +- CmsBlogFeatures + +## Extensões de entidade + +Verifique a seção "Extensões de Entidade" da documentação do módulo Kit CMS (Index.md#entity-extensions) para saber como estender as entidades da funcionalidade de Blogging do módulo Kit CMS. \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Comments.md b/docs/pt-BR/Modules/Cms-Kit/Comments.md new file mode 100644 index 0000000000..b2bd1ad573 --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Comments.md @@ -0,0 +1,144 @@ +# Kit CMS: Comentários + +O Kit CMS fornece um sistema de **comentários** para adicionar a funcionalidade de comentários a qualquer tipo de recurso, como postagens de blog, produtos, etc. + +## Habilitando a funcionalidade de comentários + +Por padrão, as funcionalidades do Kit CMS estão desabilitadas. Portanto, você precisa habilitar as funcionalidades desejadas antes de começar a usá-lo. Você pode usar o sistema de [Funcionalidades Globais](../../Global-Features.md) para habilitar/desabilitar as funcionalidades do Kit CMS durante o desenvolvimento. Alternativamente, você pode usar o [Sistema de Funcionalidades](https://docs.abp.io/pt-BR/abp/latest/Features) do ABP Framework para desabilitar uma funcionalidade do Kit CMS em tempo de execução. + +> Verifique a seção ["Como instalar" da documentação do módulo Kit CMS](Index.md#como-instalar) para saber como habilitar/desabilitar as funcionalidades do Kit CMS durante o desenvolvimento. + +## Opções + +O sistema de comentários fornece um mecanismo para agrupar definições de comentários por tipos de entidade. Por exemplo, se você deseja usar o sistema de comentários para postagens de blog e produtos, você precisa definir dois tipos de entidade chamados `BlogPosts` e `Product` e adicionar comentários sob esses tipos de entidade. + +`CmsKitCommentOptions` pode ser configurado na camada de domínio, no método `ConfigureServices` do seu [módulo](https://docs.abp.io/pt-BR/abp/latest/Module-Development-Basics). Exemplo: + +```csharp +Configure<CmsKitCommentOptions>(options => +{ + options.EntityTypes.Add(new CommentEntityTypeDefinition("Product")); + options.IsRecaptchaEnabled = true; //false por padrão + options.AllowedExternalUrls = new Dictionary<string, List<string>> + { + { + "Product", + new List<string> + { + "https://abp.io/" + } + } + }; +}); +``` + +> Se você estiver usando a [Funcionalidade de Blog](Blogging.md), o ABP Framework define automaticamente um tipo de entidade para a funcionalidade de blog. Você pode facilmente substituir ou remover os tipos de entidade predefinidos no método `Configure` como mostrado acima. + +Propriedades de `CmsKitCommentOptions`: + +- `EntityTypes`: Lista de tipos de entidade (`CmsKitCommentOptions`) definidos no sistema de comentários. +- `IsRecaptchaEnabled`: Esta flag habilita ou desabilita o reCaptcha para o sistema de comentários. Você pode defini-la como **true** se quiser usar o reCaptcha em seu sistema de comentários. +- `AllowedExternalUrls`: Indica as URLs externas permitidas por tipos de entidade, que podem ser incluídas em um comentário. Se for especificado para um determinado tipo de entidade, apenas as URLs externas especificadas serão permitidas nos comentários. + +Propriedades de `CommentEntityTypeDefinition`: + +- `EntityType`: Nome do tipo de entidade. + +## O Widget de Comentários + +O sistema de comentários fornece um [widget](../../UI/AspNetCore/Widgets.md) de comentários para permitir que os usuários enviem comentários para recursos em sites públicos. Você pode simplesmente colocar o widget em uma página como abaixo. + +```csharp +@await Component.InvokeAsync(typeof(CommentingViewComponent), new +{ + entityType = "Product", + entityId = "...", + isReadOnly = false, + referralLinks = new [] {"nofollow"} +}) +``` + +`entityType` foi explicado na seção anterior. `entityId` deve ser o id único do produto, neste exemplo. Se você tiver uma entidade Produto, você pode usar seu Id aqui. `referralLinks` é um parâmetro opcional. Você pode usar este parâmetro para adicionar valores (como "nofollow", "noreferrer" ou qualquer outro valor) aos atributos [rel](https://developer.mozilla.org/pt-BR/docs/Web/HTML/Attributes/rel) dos links. + +## Interface do Usuário + +### Itens de Menu + +Os seguintes itens de menu são adicionados pela funcionalidade de comentários à aplicação de administração: + +* **Comentários**: Abre a página de gerenciamento de comentários. + +### Páginas + +#### Gerenciamento de Comentários + +Você pode visualizar e gerenciar comentários nesta página. + +![comment-page](../../images/cmskit-module-comment-page.png) + +Você também pode visualizar e gerenciar respostas nesta página. + +![comments-detail](../../images/cmskit-module-comments-detail.png) + +## Internos + +### Camada de Domínio + +#### Agregados + +Este módulo segue o guia de [Melhores Práticas e Convenções de Entidades](https://docs.abp.io/pt-BR/abp/latest/Best-Practices/Entities). + +##### Comentário + +Um comentário representa um comentário escrito por um usuário. + +- `Comment` (raiz do agregado): Representa um comentário escrito no sistema. + +#### Repositórios + +Este módulo segue o guia de [Melhores Práticas e Convenções de Repositórios](https://docs.abp.io/pt-BR/abp/latest/Best-Practices/Repositories). + +Os seguintes repositórios personalizados são definidos para esta funcionalidade: + +- `ICommentRepository` + +#### Serviços de Domínio + +Este módulo segue o guia de [Melhores Práticas e Convenções de Serviços de Domínio](https://docs.abp.io/pt-BR/abp/latest/Best-Practices/Domain-Services). + +##### Gerenciador de Comentários + +`CommentManager` é usado para realizar algumas operações para a raiz do agregado `Comment`. + +### Camada de Aplicação + +#### Serviços de Aplicação + +- `CommentAdminAppService` (implementa `ICommentAdminAppService`): Implementa os casos de uso do sistema de gerenciamento de comentários, como listar ou remover comentários, etc. +- `CommentPublicAppService` (implementa `ICommentPublicAppService`): Implementa os casos de uso do sistema de gerenciamento de comentários em sites públicos, como listar comentários, adicionar comentários, etc. + +### Provedores de Banco de Dados + +#### Comum + +##### Prefixo de tabela / coleção e esquema + +Todas as tabelas/coleções usam o prefixo `Cms` por padrão. Defina propriedades estáticas na classe `CmsKitDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de conexão + +Este módulo usa `CmsKit` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será usada a string de conexão `Default`. + +Consulte a documentação de [strings de conexão](https://docs.abp.io/pt-BR/abp/latest/Connection-Strings) para obter detalhes. + +#### Entity Framework Core + +##### Tabelas + +- CmsComments + +#### MongoDB + +##### Coleções + +- **CmsComments** \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Dynamic-Widget.md b/docs/pt-BR/Modules/Cms-Kit/Dynamic-Widget.md new file mode 100644 index 0000000000..1bcb87346d --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Dynamic-Widget.md @@ -0,0 +1,137 @@ +# Widget Dinâmico + +O kit CMS fornece um [widget](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Widgets) dinâmico usado para renderizar os componentes previamente desenvolvidos pelo software no conteúdo das páginas e postagens de blog. Isso significa que você pode usar conteúdo dinâmico em conteúdo estático. Vamos mencionar como você pode fazer isso. Você tem duas opções para definir o widget no sistema: escrevendo e usando a interface do usuário. + +### Adicionando o widget +Primeiramente, mostraremos como usar o sistema de widgets escrevendo manualmente no conteúdo das páginas e postagens de blog. + +Vamos definir o componente de visualização + +```csharp +[Widget] +[ViewComponent(Name = "CmsToday")] +public class TodayViewComponent : AbpViewComponent +{ + public IViewComponentResult Invoke() + { + return View("~/ViewComponents/Today.cshtml", + new TodayViewComponent()); + } +} +``` + +```html +@model Volo.CmsKit.ViewComponents.TodayViewComponent + +<p>Bem-vindo ao componente de hoje</p> +<p>@DateTime.Now.ToString()</p> + +``` + +Agora é hora de configurar no arquivo YourModule.cs +```csharp +Configure<CmsKitContentWidgetOptions>(options => + { + options.AddWidget("Today","CmsToday"); + }); +``` + +Agora você está pronto para adicionar seu widget escrevendo. +[Widget Type="Today"] + +Após concluir as etapas acima, você pode ver a saída à direita da captura de tela abaixo. +![cmskit-without-parameter.png](../../images/cmskit-without-parameter.png) + +### Adicionando usando a interface do usuário +Agora mencionaremos a segunda opção, usando a interface do usuário. +Uma vez que escrever essas definições pode resultar em alguns erros, adicionamos um novo recurso para usar o sistema de widgets facilmente. À direita do editor, você verá o botão `W` personalizado para adicionar um widget dinâmico, como na imagem abaixo. Não se esqueça, por favor, que este é o modo de design e você precisa visualizar sua página no modo de visualização após salvar. Além disso, a guia `Preview` no editor estará pronta para verificar sua saída facilmente para configurações de widget nos recursos seguintes. + +![cms-kit-page-editor](../../images/cms-kit-page-editor.png) + +### Adicionando usando a interface do usuário com parâmetros +Vamos melhorar o exemplo acima adicionando um novo parâmetro chamado formato. Com esse recurso, podemos usar o sistema de widgets com muitos cenários diferentes, mas sem prolongar o documento. Além disso, esses exemplos podem ser expandidos com injeção de dependência e obtenção de valores do banco de dados, mas usaremos um exemplo básico. Vamos adicionar o parâmetro de formato para personalizar a data. + +```csharp +[Widget] +[ViewComponent(Name = "CmsToday")] +public class TodayViewComponent : AbpViewComponent +{ + public string Format { get; set; } + + public IViewComponentResult Invoke(string format) + { + return View("~/ViewComponents/Today.cshtml", + new TodayViewComponent() { Format = format }); + } +} +``` + +```html +@model Volo.CmsKit.ViewComponents.TodayViewComponent + +<p>Bem-vindo ao componente de hoje</p> +<p>@DateTime.Now.ToString(Format)</p> + +``` + +Vamos definir o componente de formato. +```csharp +[Widget] +[ViewComponent(Name = "Format")] +public class FormatViewComponent : AbpViewComponent +{ + public IViewComponentResult Invoke() + { + return View("~/ViewComponents/Format.cshtml", + new FormatViewModel()); + } +} + +public class FormatViewModel +{ + [DisplayName("Formate sua data no componente")] + public string Format { get; set; } +} +``` +> Nota importante: Para obter as propriedades corretamente, você deve definir a propriedade `name` na página razor ou pode usar o componente ABP. O ABP lida com isso automaticamente. + +```html +@using Volo.CmsKit.ViewComponents +@model FormatViewModel + +<div> + <abp-input asp-for="Format" /> +</div> +``` + +```csharp +Configure<CmsKitContentWidgetOptions>(options => + { + options.AddWidget("Today", "CmsToday", "Format"); + }); +``` + +![cmskit-module-editor-parameter](../../images/cmskit-module-editor-parameter.png) + +Nesta imagem, após escolher seu widget (em outro caso, ele muda automaticamente de acordo com sua configuração, o meu é `Today`. Seu nome de parâmetro é `parameterWidgetName` e seu valor é `Format`) você verá o próximo widget. Insira valores de entrada ou escolha-os e clique em `Add`. Você verá a saída sublinhada no editor. À direita da imagem, você também pode ver sua saída pré-visualizada. + +Você pode editar essa saída manualmente se fizer algum código errado para isso (valor incorreto ou erro de digitação), você não verá o widget, mesmo assim, sua página será visualizada com sucesso. + +## Opções +Para configurar o widget, você deve definir o código abaixo no arquivo YourModule.cs + +```csharp +Configure<CmsKitContentWidgetOptions>(options => + { + options.AddWidget(widgetType: "Today", widgetName: "CmsToday", parameterWidgetName: "Format"); + }); +``` + +Vamos analisar esses parâmetros em detalhes +* `widgetType` é usado para o usuário final e nomes mais legíveis. A palavra em negrito a seguir representa o widgetType. +[Widget Type="**Today**" Format="yyyy-dd-mm HH:mm:ss"]. + +* `widgetName` é usado para o nome do seu widget usado no código para o nome do `ViewComponent`. + +* `parameterWidgetName` é usado no lado do componente do editor para ver no modal `Add Widget`. +Após escolher o tipo de widget na lista suspensa (agora apenas definido `Format`) e renderizar este widget automaticamente. É necessário apenas para ver a interface do usuário uma vez usando parâmetros. \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Global-Resources.md b/docs/pt-BR/Modules/Cms-Kit/Global-Resources.md new file mode 100644 index 0000000000..299d00e420 --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Global-Resources.md @@ -0,0 +1,72 @@ +# CMS Kit: Recursos Globais + +O sistema de Recursos Globais do CMS Kit permite adicionar estilos e scripts globais dinamicamente. + +## Habilitando o Recurso de Recursos Globais + +Por padrão, os recursos do CMS Kit estão desabilitados. Portanto, você precisa habilitar os recursos que deseja antes de começar a usá-lo. Você pode usar o sistema de [Recursos Globais](../../Global-Features.md) para habilitar/desabilitar os recursos do CMS Kit durante o desenvolvimento. Alternativamente, você pode usar o [Sistema de Recursos](https://docs.abp.io/en/abp/latest/Features) do ABP Framework para desabilitar um recurso do CMS Kit em tempo de execução. + +> Verifique a seção "Como Instalar" da documentação do Módulo CMS Kit para saber como habilitar/desabilitar os recursos do CMS Kit durante o desenvolvimento. + +## A Interface do Usuário + +### Itens do Menu + +O módulo CMS Kit adiciona os seguintes itens ao menu principal, sob o item de menu *Recursos Globais*: + +* **Recursos Globais**: Página de gerenciamento de recursos globais. + +A classe `CmsKitAdminMenus` possui as constantes para os nomes dos itens do menu. + +### Página de Recursos Globais + +A página de Recursos Globais é usada para gerenciar estilos e scripts globais no sistema. + +![cms-kit-global-resources-page](../../images/cmskit-module-global-resources-page.png) + +# Internos + +## Camada de Domínio + +#### Agregados + +Este módulo segue o guia de [Melhores Práticas e Convenções de Entidades](https://docs.abp.io/en/abp/latest/Best-Practices/Entities). + +- `GlobalResource` (raiz do agregado): Armazena um recurso. + +#### Repositórios + +Este módulo segue o guia de [Melhores Práticas e Convenções de Repositórios](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories). + +Os seguintes repositórios personalizados são definidos para este recurso: + +- `IGlobalResourceRepository` + +#### Serviços de Domínio + +Este módulo segue o guia de [Melhores Práticas e Convenções de Serviços de Domínio](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services). + +##### Gerenciador de Recursos Globais + +`GlobalResourceManager` é usado para realizar operações para a raiz do agregado `GlobalResource`. + +### Camada de Aplicação + +#### Serviços de Aplicação + +- `GlobalResourceAdminAppService` (implementa `IGlobalResourceAdminAppService`): Implementa as operações de gerenciamento do sistema de recursos globais. +- `GlobalResourcePublicAppService` (implementa `IGlobalResourcePublicAppService`): Implementa os casos de uso públicos do sistema de recursos globais. + +#### Banco de Dados + +#### Entity Framework Core + +##### Tabelas + +- CmsGlobalResources + +#### MongoDB + +##### Coleções + +- CmsGlobalResources \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Index.md b/docs/pt-BR/Modules/Cms-Kit/Index.md new file mode 100644 index 0000000000..cd6908b317 --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Index.md @@ -0,0 +1,143 @@ +# Módulo CMS Kit + +Este módulo fornece recursos de CMS (Sistema de Gerenciamento de Conteúdo) para sua aplicação. Ele fornece **blocos de construção principais** e **sub-sistemas** totalmente funcionais para criar seu próprio site com recursos de CMS habilitados, ou usar os blocos de construção em seus sites com qualquer finalidade. + +> **Este módulo está atualmente disponível apenas para a interface MVC / Razor Pages**. Embora não haja um pacote oficial do Blazor, ele também pode funcionar em uma interface Blazor Server, pois uma interface Blazor Server é na verdade um aplicativo híbrido que é executado em um aplicativo ASP.NET Core MVC / Razor Pages. + +Os seguintes recursos estão atualmente disponíveis: + +* Fornece um sistema de gerenciamento de [**páginas**](Pages.md) para gerenciar páginas dinâmicas com URLs dinâmicos. +* Fornece um sistema de [**blog**](Blogging.md) para criar e publicar postagens de blog com suporte a vários blogs. +* Fornece um sistema de [**marcação**](Tags.md) para marcar qualquer tipo de recurso, como uma postagem de blog. +* Fornece um sistema de [**comentários**](Comments.md) para adicionar recursos de comentários a qualquer tipo de recurso, como postagem de blog ou uma página de avaliação de produto. +* Fornece um sistema de [**reações**](Reactions.md) para adicionar recursos de reações (emojis) a qualquer tipo de recurso, como uma postagem de blog ou um comentário. +* Fornece um sistema de [**classificação**](Ratings.md) para adicionar recursos de classificação a qualquer tipo de recurso. +* Fornece um sistema de [**menu**](Menus.md) para gerenciar menus públicos dinamicamente. +* Fornece um sistema de [**recursos globais**](Global-Resources.md) para adicionar estilos e scripts globais dinamicamente. +* Fornece um sistema de [**widget dinâmico**](Dynamic-Widget.md) para criar widgets dinâmicos para páginas e postagens de blog. + +> Você pode clicar nos links de recursos acima para entender e aprender como usá-los. + +Todos os recursos podem ser usados individualmente. Se você desabilitar um recurso, ele desaparecerá completamente de sua aplicação, inclusive das tabelas do banco de dados, com a ajuda do sistema [Recursos Globais](../../Global-Features.md). + +## Pré-requisitos + +- Este módulo depende do módulo [BlobStoring](../../Blob-Storing.md) para armazenar conteúdo de mídia. +> Certifique-se de que o módulo `BlobStoring` esteja instalado e que pelo menos um provedor esteja configurado corretamente. Para obter mais informações, consulte a [documentação](../../Blob-Storing.md). + +- O CMS Kit usa o [cache distribuído](../../Caching.md) para responder mais rapidamente. +> É altamente recomendado usar um cache distribuído, como o [Redis](../../Redis-Cache.md), para garantir a consistência dos dados em implantações distribuídas/clusterizadas. + +## Como instalar + +O [ABP CLI](../../CLI.md) permite instalar um módulo em uma solução usando o comando `add-module`. Você pode instalar o módulo CMS Kit em um terminal de linha de comando com o seguinte comando: + +```bash +abp add-module Volo.CmsKit --skip-db-migrations +``` + +> Por padrão, o Cms-Kit está desabilitado pelo `GlobalFeature`. Por causa disso, a migração inicial estará vazia. Portanto, você pode pular a migração adicionando `--skip-db-migrations` ao comando de instalação se estiver usando o Entity Framework Core. Após habilitar o recurso global Cms-Kit, adicione uma nova migração. + +Após o processo de instalação, abra a classe `GlobalFeatureConfigurator` no projeto `Domain.Shared` de sua solução e coloque o seguinte código no método `Configure` para habilitar todos os recursos no módulo CMS Kit. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.EnableAll(); +}); +``` + +Em vez de habilitar todos, você pode preferir habilitar os recursos um por um. O exemplo a seguir habilita apenas os recursos de [marcadores](Tags.md) e [comentários](Comments.md): + +````csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.Tags.Enable(); + cmsKit.Comments.Enable(); +}); +```` + +> Se você estiver usando o Entity Framework Core, não se esqueça de adicionar uma nova migração e atualizar seu banco de dados. + +## Os Pacotes + +Este módulo segue o [guia de melhores práticas para desenvolvimento de módulos](https://docs.abp.io/en/abp/latest/Best-Practices/Index) e consiste em vários pacotes NuGet e NPM. Consulte o guia se você quiser entender os pacotes e as relações entre eles. + +Os pacotes do CMS kit são projetados para vários cenários de uso. Se você verificar os [pacotes do CMS kit](https://www.nuget.org/packages?q=Volo.CmsKit), verá que alguns pacotes têm sufixos `Admin` e `Public`. A razão é que o módulo possui duas camadas de aplicação, considerando que eles podem ser usados em diferentes tipos de aplicativos. Essas camadas de aplicação usam uma única camada de domínio: + + - Os pacotes `Volo.CmsKit.Admin.*` contêm as funcionalidades necessárias para aplicativos de administração (back office). + - Os pacotes `Volo.CmsKit.Public.*` contêm as funcionalidades usadas em sites públicos onde os usuários leem postagens de blog ou deixam comentários. + - Os pacotes `Volo.CmsKit.*` (sem sufixo Admin/Public) são chamados de pacotes unificados. Os pacotes unificados são atalhos para adicionar pacotes Admin e Public (da camada relacionada) separadamente. Se você tiver um único aplicativo para administração e site público, você pode usar esses pacotes. + +## Internos + +### Prefixo de tabela / coleção e esquema + +Todas as tabelas/coleções usam o prefixo `Cms` por padrão. Defina propriedades estáticas na classe `CmsKitDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo provedor de banco de dados). + +### String de conexão + +Este módulo usa `CmsKit` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será substituída pela string de conexão `Default`. + +Consulte a documentação sobre [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter mais detalhes. + +## Extensões de Entidade + +O sistema de extensão de entidade do módulo (https://docs.abp.io/en/abp/latest/Module-Entity-Extensions) é um sistema de extensão **de alto nível** que permite **definir novas propriedades** para entidades existentes dos módulos dependentes. Ele automaticamente **adiciona propriedades à entidade**, **banco de dados**, **API HTTP** e **interface do usuário** em um único ponto. + +Para estender as entidades do módulo CMS Kit, abra a classe `YourProjectNameModuleExtensionConfigurator` dentro do projeto `DomainShared` e altere o método `ConfigureExtraProperties` conforme mostrado abaixo. + +```csharp +public static void ConfigureExtraProperties() +{ + OneTimeRunner.Run(() => + { + ObjectExtensionManager.Instance.Modules() + .ConfigureCmsKit(cmsKit => + { + cmsKit.ConfigureBlog(plan => // estende a entidade Blog + { + plan.AddOrUpdateProperty<string>( //tipo de propriedade: string + "BlogDescription", //nome da propriedade + property => { + //regras de validação + property.Attributes.Add(new RequiredAttribute()); //adiciona o atributo obrigatório à propriedade definida + + //...outras configurações para esta propriedade + } + ); + }); + + cmsKit.ConfigureBlogPost(blogPost => // estende a entidade BlogPost + { + blogPost.AddOrUpdateProperty<string>( //tipo de propriedade: string + "BlogPostDescription", //nome da propriedade + property => { + //regras de validação + property.Attributes.Add(new RequiredAttribute()); //adiciona o atributo obrigatório à propriedade definida + property.Attributes.Add( + new StringLengthAttribute(MyConsts.MaximumDescriptionLength) { + MinimumLength = MyConsts.MinimumDescriptionLength + } + ); + + //...outras configurações para esta propriedade + } + ); + }); + }); + }); +} +``` + +* O método `ConfigureCmsKit(...)` é usado para configurar as entidades do módulo CMS Kit. + +* `cmsKit.ConfigureBlog(...)` é usado para configurar a entidade **Blog** do módulo CMS Kit. Você pode adicionar ou atualizar suas propriedades extras na entidade **Blog**. + +* `cmsKit.ConfigureBlogPost(...)` é usado para configurar a entidade **BlogPost** do módulo CMS Kit. Você pode adicionar ou atualizar suas propriedades extras na entidade **BlogPost**. + +* Você também pode definir algumas regras de validação para a propriedade que você definiu. No exemplo acima, foram adicionados `RequiredAttribute` e `StringLengthAttribute` para a propriedade chamada **"BlogPostDescription"**. + +* Quando você define a nova propriedade, ela será automaticamente adicionada à **Entidade**, **API HTTP** e **UI** para você. + * Depois de definir uma propriedade, ela aparece nos formulários de criação e atualização da entidade relacionada. + * As novas propriedades também aparecem na tabela de dados da página relacionada. \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Menus.md b/docs/pt-BR/Modules/Cms-Kit/Menus.md new file mode 100644 index 0000000000..297a9ea2ff --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Menus.md @@ -0,0 +1,90 @@ +# CMS Kit: Menus + +O sistema de menus do CMS Kit permite gerenciar menus públicos de forma dinâmica. + +## Habilitando o recurso de menu + +Por padrão, os recursos do CMS Kit estão desabilitados. Portanto, é necessário habilitar os recursos desejados antes de começar a usá-lo. Você pode usar o sistema de [Recursos Globais](../../Global-Features.md) para habilitar/desabilitar os recursos do CMS Kit durante o desenvolvimento. Alternativamente, você pode usar o [Sistema de Recursos](https://docs.abp.io/en/abp/latest/Features) do ABP Framework para desabilitar um recurso do CMS Kit em tempo de execução. + +> Verifique a seção "Como instalar" da documentação do módulo CMS Kit (Index.md#how-to-install) para saber como habilitar/desabilitar os recursos do CMS Kit durante o desenvolvimento. + +## A interface do usuário + +### Itens de menu + +O módulo CMS Kit adiciona os seguintes itens ao menu principal, sob o item de menu *CMS*: + +* **Menus**: Página de gerenciamento de menus. + +A classe `CmsKitAdminMenus` possui as constantes para os nomes dos itens de menu. + +### Menus + +#### Gerenciamento de menus + +A página de menus é usada para gerenciar menus públicos dinâmicos no sistema. + +![cms-kit-menus-page](../../images/cmskit-module-menus-page.png) + +Os itens de menu criados serão visíveis no lado público do site, como mostrado abaixo: + +![cms-kit-public-menus](../../images//cmskit-module-menus-public.png) + +## Internos + +### Camada de domínio + +#### Agregados + +Este módulo segue o guia de [Melhores Práticas e Convenções de Entidades](https://docs.abp.io/en/abp/latest/Best-Practices/Entities). + +- `MenuItem` (raiz do agregado): Um item de menu representa um único nó na árvore de menus. + +#### Repositórios + +Este módulo segue o guia de [Melhores Práticas e Convenções de Repositórios](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories). + +Os seguintes repositórios personalizados são definidos para este recurso: + +- `IMenuItemRepository` + +#### Serviços de domínio + +Este módulo segue o guia de [Melhores Práticas e Convenções de Serviços de Domínio](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services). + +##### Gerenciador de itens de menu + +`MenuItemManager` é usado para realizar algumas operações para a raiz do agregado `MenuItemManager`. + +### Camada de aplicação + +#### Serviços de aplicação + +- `MenuItemAdminAppService` (implementa `IMenuItemAdminAppService`): Implementa as operações de gerenciamento do sistema de menus. +- `MenuItemPublicAppService` (implementa `IMenuItemPublicAppService`): Implementa os casos de uso públicos do sistema de menus. + +### Provedores de banco de dados + +#### Comum + +##### Prefixo de tabela / coleção e esquema + +Todas as tabelas/coleções usam o prefixo `Cms` por padrão. Defina propriedades estáticas na classe `CmsKitDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de conexão + +Este módulo usa `CmsKit` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será substituída pela string de conexão `Default`. + +Consulte a documentação sobre [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter mais detalhes. + +#### Entity Framework Core + +##### Tabelas + +- CmsMenuItems + +#### MongoDB + +##### Coleções + +- CmsMenuItems \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Pages.md b/docs/pt-BR/Modules/Cms-Kit/Pages.md new file mode 100644 index 0000000000..1d99a9a611 --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Pages.md @@ -0,0 +1,33 @@ +# Kit CMS: Páginas + +O sistema de páginas do Kit CMS permite que você crie páginas dinâmicas especificando URLs, que é a funcionalidade fundamental de um CMS. + +## Habilitando a Funcionalidade de Páginas + +Por padrão, as funcionalidades do Kit CMS estão desabilitadas. Portanto, você precisa habilitar as funcionalidades que deseja antes de começar a usá-lo. Você pode usar o sistema de [Funcionalidades Globais](../../Global-Features.md) para habilitar/desabilitar as funcionalidades do Kit CMS durante o desenvolvimento. Alternativamente, você pode usar o [Sistema de Funcionalidades](https://docs.abp.io/en/abp/latest/Features) do ABP Framework para desabilitar uma funcionalidade do Kit CMS em tempo de execução. + +> Verifique a seção "Como Instalar" da documentação do Módulo Kit CMS (Index.md#how-to-install) para saber como habilitar/desabilitar as funcionalidades do Kit CMS durante o desenvolvimento. + +## A Interface do Usuário + +### Itens do Menu + +O módulo Kit CMS adiciona os seguintes itens ao menu principal, sob o item de menu *CMS*: + +* **Páginas**: Página de gerenciamento de páginas. + +A classe `CmsKitAdminMenus` possui as constantes para os nomes dos itens do menu. + +### Páginas + +#### Gerenciamento de Páginas + +A página **Páginas** é usada para gerenciar páginas dinâmicas no sistema. Você pode criar/editar páginas com rotas e conteúdos dinâmicos nesta página: + +![pages-edit](../../images/cmskit-module-pages-edit.png) + +Depois de criar páginas, você pode definir uma delas como página inicial. Então, sempre que alguém navegar para a página inicial do seu aplicativo, eles verão o conteúdo dinâmico da página que você definiu nesta página. + +![pages-page](../../images/cmskit-module-pages-page.png) + +Além disso, quando você cria uma página, você pode acessá-la através da URL `/{slug}`. \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Ratings.md b/docs/pt-BR/Modules/Cms-Kit/Ratings.md new file mode 100644 index 0000000000..421a4c3051 --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Ratings.md @@ -0,0 +1,111 @@ +# Sistema de Avaliação + +O kit CMS fornece um sistema de **avaliação** para adicionar recursos de avaliação a qualquer tipo de recurso, como postagens de blog, comentários, etc. Aqui está como o componente de avaliação se parece em uma página de exemplo: + +![avaliações](../../images/cmskit-module-ratings.png) + +## Habilitando o Recurso de Avaliação + +Por padrão, os recursos do CMS Kit estão desabilitados. Portanto, você precisa habilitar os recursos que deseja antes de começar a usá-lo. Você pode usar o sistema de [Recurso Global](../../Global-Features.md) para habilitar/desabilitar os recursos do CMS Kit durante o desenvolvimento. Alternativamente, você pode usar o [Sistema de Recursos](https://docs.abp.io/en/abp/latest/Features) do ABP Framework para desabilitar um recurso do CMS Kit em tempo de execução. + +> Verifique a seção ["Como Instalar" da documentação do Módulo CMS Kit](Index.md#how-to-install) para ver como habilitar/desabilitar os recursos do CMS Kit durante o desenvolvimento. + +## Opções + +O sistema de avaliação fornece um mecanismo para agrupar avaliações por tipos de entidade. Por exemplo, se você deseja usar o sistema de avaliação para produtos, você precisa definir um tipo de entidade chamado `Produto` e, em seguida, adicionar avaliações sob o tipo de entidade definido. + +`CmsKitRatingOptions` pode ser configurado na camada de domínio, no método `ConfigureServices` do seu [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics). Exemplo: + +```csharp +Configure<CmsKitRatingOptions>(options => +{ + options.EntityTypes.Add(new RatingEntityTypeDefinition("Produto")); +}); +``` + +> Se você estiver usando o [Recurso de Blog](Blogging.md), o framework ABP define automaticamente um tipo de entidade para o recurso de blog. Você pode facilmente substituir ou remover os tipos de entidade predefinidos no método `Configure` como mostrado acima. + +Propriedades de `CmsKitRatingOptions`: + +- `EntityTypes`: Lista de tipos de entidade definidos (`RatingEntityTypeDefinition`) no sistema de avaliação. + +Propriedades de `RatingEntityTypeDefinition`: + +- `EntityType`: Nome do tipo de entidade. + +## O Widget de Avaliação + +O sistema de avaliação fornece um widget de avaliação para permitir que os usuários enviem avaliações para recursos em sites públicos. Você pode simplesmente colocar o widget em uma página como abaixo. + +```csharp +@await Component.InvokeAsync(typeof(RatingViewComponent), new +{ + entityType = "Produto", + entityId = "entityId", + isReadOnly = false +}) +``` + +`entityType` foi explicado na seção anterior. `entityId` deve ser o id único do produto, neste exemplo. Se você tiver uma entidade Produto, você pode usar seu Id aqui. + +# Internos + +## Camada de Domínio + +#### Agregados + +Este módulo segue o guia de [Melhores Práticas e Convenções de Entidades](https://docs.abp.io/en/abp/latest/Best-Practices/Entities). + +##### Avaliação + +Uma avaliação representa uma avaliação dada por um usuário. + +- `Avaliação` (raiz do agregado): Representa uma avaliação dada no sistema. + +#### Repositórios + +Este módulo segue o guia de [Melhores Práticas e Convenções de Repositórios](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories). + +Os seguintes repositórios personalizados são definidos para este recurso: + +- `IRatingRepository` + +#### Serviços de Domínio + +Este módulo segue o guia de [Melhores Práticas e Convenções de Serviços de Domínio](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services). + +##### Gerenciador de Reações + +`RatingManager` é usado para realizar algumas operações para a raiz do agregado `Avaliação`. + +### Camada de Aplicação + +#### Serviços de Aplicação + +- `RatingPublicAppService` (implementa `IRatingPublicAppService`): Implementa os casos de uso do sistema de avaliação. + +### Provedores de Banco de Dados + +#### Comum + +##### Prefixo da tabela / coleção e esquema + +Todas as tabelas/coleções usam o prefixo `Cms` por padrão. Defina propriedades estáticas na classe `CmsKitDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de conexão + +Este módulo usa `CmsKit` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será usada a string de conexão `Default`. + +Consulte a documentação de [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter detalhes. + +#### Entity Framework Core + +##### Tabelas + +- CmsRatings + +#### MongoDB + +##### Coleções + +- **CmsRatings** \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Reactions.md b/docs/pt-BR/Modules/Cms-Kit/Reactions.md new file mode 100644 index 0000000000..5aeedf6df0 --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Reactions.md @@ -0,0 +1,126 @@ +# Sistema de Reações + +O kit CMS fornece um sistema de **reações** para adicionar recursos de reações a qualquer tipo de recurso, como postagens de blog ou comentários. + +O componente de reação permite que os usuários reajam ao seu conteúdo por meio de ícones/emojis pré-definidos. Aqui está como o componente de reações pode parecer: + +![reactions](../../images/cmskit-module-reactions.png) + +Você também pode personalizar os ícones de reação mostrados no componente de reação. + +## Habilitando o Recurso de Reação + +Por padrão, os recursos do CMS Kit estão desabilitados. Portanto, você precisa habilitar os recursos que deseja antes de começar a usá-lo. Você pode usar o sistema de [Recurso Global](../../Global-Features.md) para habilitar/desabilitar recursos do CMS Kit durante o desenvolvimento. Alternativamente, você pode usar o [Sistema de Recursos](https://docs.abp.io/en/abp/latest/Features) do ABP Framework para desabilitar um recurso do CMS Kit em tempo de execução. + +> Verifique a seção ["Como Instalar" da documentação do Módulo CMS Kit](Index.md#how-to-install) para ver como habilitar/desabilitar recursos do CMS Kit durante o desenvolvimento. + +## Opções + +O sistema de reação fornece um mecanismo para agrupar reações por tipos de entidade. Por exemplo, se você deseja usar o sistema de reação para produtos, você precisa definir um tipo de entidade chamado `Produto` e, em seguida, adicionar reações sob o tipo de entidade definido. + +`CmsKitReactionOptions` pode ser configurado na camada de domínio, no método `ConfigureServices` do seu [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics). Exemplo: + +```csharp +Configure<CmsKitReactionOptions>(options => +{ + options.EntityTypes.Add( + new ReactionEntityTypeDefinition( + "Produto", + reactions: new[] + { + new ReactionDefinition(StandardReactions.Smile), + new ReactionDefinition(StandardReactions.ThumbsUp), + new ReactionDefinition(StandardReactions.ThumbsDown), + new ReactionDefinition(StandardReactions.Confused), + new ReactionDefinition(StandardReactions.Eyes), + new ReactionDefinition(StandardReactions.Heart) + })); +}); +``` + +> Se você estiver usando os recursos [Comentário](Comments.md) ou [Blogging](Blogging.md), o framework ABP define reações predefinidas para esses recursos automaticamente. + +Propriedades de `CmsKitReactionOptions`: + +- `EntityTypes`: Lista de tipos de entidade definidos (`CmsKitReactionOptions`) no sistema de reação. + +Propriedades de `ReactionEntityTypeDefinition`: + +- `EntityType`: Nome do tipo de entidade. +- `Reactions`: Lista de reações definidas (`ReactionDefinition`) no tipo de entidade. + +## O Widget de Reações + +O sistema de reação fornece um widget de reação para permitir que os usuários enviem reações para recursos. Você pode colocar o widget em uma página da seguinte forma: + +```csharp +@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new +{ + entityType = "Produto", + entityId = "..." +}) +``` + +`entityType` foi explicado na seção anterior. `entityId` deve ser o id único do produto, neste exemplo. Se você tiver uma entidade Produto, pode usar seu Id aqui. + +# Internos + +## Camada de Domínio + +#### Agregados + +Este módulo segue o guia de [Melhores Práticas e Convenções de Entidades](https://docs.abp.io/en/abp/latest/Best-Practices/Entities). + +##### UserReaction + +Uma reação do usuário representa uma reação específica de um usuário. + +- `UserReaction` (raiz do agregado): Representa uma reação específica no sistema. + +#### Repositórios + +Este módulo segue o guia de [Melhores Práticas e Convenções de Repositórios](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories). + +Os seguintes repositórios personalizados são definidos para este recurso: + +- `IUserReactionRepository` + +#### Serviços de Domínio + +Este módulo segue o guia de [Melhores Práticas e Convenções de Serviços de Domínio](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services). + +##### Reaction Manager + +`ReactionManager` é usado para realizar algumas operações para a raiz do agregado `UserReaction`. + +### Camada de Aplicação + +#### Serviços de Aplicação + +- `ReactionPublicAppService` (implementa `IReactionPublicAppService`): Implementa os casos de uso do sistema de reação. + +### Provedores de Banco de Dados + +#### Comum + +##### Prefixo da tabela/collection e esquema + +Todas as tabelas/collections usam o prefixo `Cms` por padrão. Defina propriedades estáticas na classe `CmsKitDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de conexão + +Este módulo usa `CmsKit` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será usada a string de conexão `Default`. + +Consulte a documentação de [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter detalhes. + +#### Entity Framework Core + +##### Tabelas + +- CmsUserReactions + +#### MongoDB + +##### Coleções + +- **CmsUserReactions** \ No newline at end of file diff --git a/docs/pt-BR/Modules/Cms-Kit/Tags.md b/docs/pt-BR/Modules/Cms-Kit/Tags.md new file mode 100644 index 0000000000..e4d04cc89a --- /dev/null +++ b/docs/pt-BR/Modules/Cms-Kit/Tags.md @@ -0,0 +1,165 @@ +# Gerenciamento de Tags + +O CMS Kit fornece um sistema de **tag** para marcar qualquer tipo de recurso, como uma postagem de blog. + +## Habilitando o recurso de Gerenciamento de Tags + +Por padrão, os recursos do CMS Kit estão desabilitados. Portanto, você precisa habilitar os recursos que deseja antes de começar a usá-lo. Você pode usar o sistema de [Recursos Globais](../../Global-Features.md) para habilitar/desabilitar os recursos do CMS Kit durante o desenvolvimento. Alternativamente, você pode usar o [Sistema de Recursos](https://docs.abp.io/en/abp/latest/Features) do ABP Framework para desabilitar um recurso do CMS Kit em tempo de execução. + +> Verifique a seção ["Como instalar" da documentação do Módulo CMS Kit](Index.md#how-to-install) para saber como habilitar/desabilitar os recursos do CMS Kit durante o desenvolvimento. + +## Opções + +O sistema de tags fornece um mecanismo para agrupar tags por tipos de entidade. Por exemplo, se você deseja usar o sistema de tags para postagens de blog e produtos, você precisa definir dois tipos de entidade chamados `BlogPosts` e `Product` e adicionar tags sob esses tipos de entidade. + +`CmsKitTagOptions` pode ser configurado na camada de domínio, no método `ConfigureServices` da sua classe [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics). + +**Exemplo: Adicionando suporte a tags para produtos** + +```csharp +Configure<CmsKitTagOptions>(options => +{ + options.EntityTypes.Add(new TagEntityTypeDefiniton("Product")); +}); +``` + +> Se você estiver usando o [Recurso de Blogging](Blogging.md), o framework ABP define automaticamente um tipo de entidade para o recurso de blog. + +Propriedades de `CmsKitTagOptions`: + +- `EntityTypes`: Lista de tipos de entidade definidos (`TagEntityTypeDefiniton`) no sistema de tags. + +Propriedades de `TagEntityTypeDefiniton`: + +- `EntityType`: Nome do tipo de entidade. +- `DisplayName`: O nome de exibição do tipo de entidade. Você pode usar um nome de exibição amigável para mostrar a definição do tipo de entidade no site de administração. +- `CreatePolicies`: Lista de nomes de políticas/permissões que permitem aos usuários criar tags sob o tipo de entidade. +- `UpdatePolicies`: Lista de nomes de políticas/permissões que permitem aos usuários atualizar tags sob o tipo de entidade. +- `DeletePolicies`: Lista de nomes de políticas/permissões que permitem aos usuários excluir tags sob o tipo de entidade. + +## O Widget de Tag + +O sistema de tags fornece um widget de tag [widget](../../UI/AspNetCore/Widgets.md) para exibir as tags associadas a um recurso que foi configurado para tags. Você pode simplesmente colocar o widget em uma página como a abaixo: + +```csharp +@await Component.InvokeAsync(typeof(TagViewComponent), new +{ + entityType = "Product", + entityId = "...", + urlFormat = "/products?tagId={TagId}&tagName={TagName}" +}) +``` + +`entityType` foi explicado na seção anterior. Neste exemplo, o `entityId` deve ser o id único do produto. Se você tiver uma entidade `Product`, você pode usar seu Id aqui. `urlFormat` é o formato de string do URL que será gerado para cada tag. Você pode usar os espaços reservados `{TagId}` e `{TagName}` para preencher o URL. Por exemplo, o formato de URL acima preencherá URLs como `/products?tagId=1&tagName=tag1`. + +## O Widget de Tags Populares + +O sistema de tags fornece um widget de tags populares [widget](../../UI/AspNetCore/Widgets.md) para exibir tags populares de um recurso que foi configurado para tags. Você pode simplesmente colocar o widget em uma página como abaixo: + +```csharp +@await Component.InvokeAsync(typeof(PopularTagsViewComponent), new +{ + entityType = "Product", + urlFormat = "/products?tagId={TagId}&tagName={TagName}", + maxCount = 10 +}) +``` + +`entityType` foi explicado na seção anterior. `urlFormat` foi explicado na seção anterior. `maxCount` é o número máximo de tags a serem exibidas. + +## Interface do Usuário + +### Itens de Menu + +Os seguintes itens de menu são adicionados pelo recurso de tags à aplicação de administração: + +* **Tags**: Abre a página de gerenciamento de tags. + +### Páginas + +#### Gerenciamento de Tags + +Esta página pode ser usada para criar, editar e excluir tags para os tipos de entidade. + +![tags-page](../../images/cmskit-module-tags-page.png) + +Você pode criar ou editar uma tag existente nesta página. + +![tag-edit](../../images/cmskit-module-tag-edit.png) + +## Internos + +### Camada de Domínio + +#### Agregados + +Este módulo segue o guia de [Melhores Práticas e Convenções de Entidades](https://docs.abp.io/en/abp/latest/Best-Practices/Entities). + +##### Tag + +Uma tag representa uma tag sob o tipo de entidade. + +- `Tag` (raiz do agregado): Representa uma tag no sistema. + +##### EntityTag + +Uma entidade tag representa uma conexão entre a tag e a entidade marcada. + +- `EntityTag`(entidade): Representa uma conexão entre a tag e a entidade marcada. + +#### Repositórios + +Este módulo segue o guia de [Melhores Práticas e Convenções de Repositórios](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories). + +Os seguintes repositórios personalizados são definidos para este recurso: + +- `ITagRepository` +- `IEntityTagRepository` + +#### Serviços de Domínio + +Este módulo segue o guia de [Melhores Práticas e Convenções de Serviços de Domínio](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services). + +##### Gerenciador de Tags + +`TagManager` realiza algumas operações para a raiz do agregado `Tag`. + +##### Gerenciador de Entidade Tag + +`EntityTagManager` realiza algumas operações para a entidade `EntityTag`. + +### Camada de Aplicação + +#### Serviços de Aplicação + +- `TagAdminAppService` (implementa `ITagAdminAppService`). +- `EntityTagAdminAppService` (implementa `IEntityTagAdminAppService`). +- `TagAppService` (implementa `ITagAppService`). + +### Provedores de Banco de Dados + +#### Comum + +##### Prefixo de Tabela / Coleção e esquema + +Todas as tabelas/coleções usam o prefixo `Cms` por padrão. Defina propriedades estáticas na classe `CmsKitDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de conexão + +Este módulo usa `CmsKit` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será usada a string de conexão `Default`. + +Consulte a documentação de [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter detalhes. + +#### Entity Framework Core + +##### Tabelas + +- CmsTags +- CmsEntityTags + +#### MongoDB + +##### Coleções + +- **CmsTags** +- **CmsEntityTags** \ No newline at end of file diff --git a/docs/pt-BR/Modules/Database-Tables.md b/docs/pt-BR/Modules/Database-Tables.md new file mode 100644 index 0000000000..be00f95247 --- /dev/null +++ b/docs/pt-BR/Modules/Database-Tables.md @@ -0,0 +1,577 @@ +# Tabelas do Banco de Dados + +Esta documentação descreve todas as tabelas do banco de dados e seus propósitos. Você pode ler esta documentação para obter conhecimento geral das tabelas do banco de dados que vêm de cada módulo. + +## [Módulo de Registro de Auditoria](Audit-Logging.md) + +### AbpAuditLogs + +Esta tabela armazena informações sobre os registros de auditoria no aplicativo. Cada registro representa um log de auditoria e rastreia as ações realizadas no aplicativo. + +### AbpAuditLogActions + +Esta tabela armazena informações sobre as ações realizadas no aplicativo, que são registradas para fins de auditoria. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpAuditLogs](#abpauditlogs) | Id | Vincula cada ação a um log de auditoria específico. | + +### AbpEntityChanges + +Esta tabela armazena informações sobre as alterações de entidade no aplicativo, que são registradas para fins de auditoria. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpAuditLogs](#abpauditlogs) | Id | Vincula cada alteração de entidade a um log de auditoria específico. | + +### AbpEntityPropertyChanges + +Esta tabela armazena informações sobre as alterações de propriedade em entidades no aplicativo, que são registradas para fins de auditoria. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpEntityChanges](#abpentitychanges) | Id | Vincula cada alteração de propriedade a uma alteração de entidade específica. | + +## [Módulo de Tarefas em Segundo Plano](Background-Jobs.md) + +### AbpBackgroundJobs + +Esta tabela armazena informações sobre as tarefas em segundo plano no aplicativo e facilita seu gerenciamento e rastreamento eficientes. Cada entrada na tabela contém detalhes de uma tarefa em segundo plano, incluindo o nome da tarefa, argumentos, contagem de tentativas, próxima tentativa, última tentativa, status abandonado e prioridade. + +## [Módulo de Gerenciamento de Inquilinos](Tenant-Management.md) + +### AbpTenants + +Esta tabela armazena informações sobre os inquilinos. Cada registro representa um inquilino e contém informações sobre o inquilino, como nome e outros detalhes. + +### AbpTenantConnectionStrings + +Esta tabela armazena informações sobre as strings de conexão do banco de dados do inquilino. Quando você define uma string de conexão para um inquilino, um novo registro será adicionado a esta tabela. Você pode consultar este banco de dados para obter strings de conexão por inquilinos. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpTenants](#abptenants) | Id | A coluna `Id` na tabela `AbpTenants` é usada para associar a string de conexão do inquilino ao inquilino correspondente. | + +## Módulo de Blogging + +### BlgUsers + +Esta tabela armazena informações sobre os usuários do blog. Quando um novo usuário de identidade é criado, um novo registro será adicionado a esta tabela. + +### BlgBlogs + +Esta tabela serve para armazenar informações do blog e separar semanticamente as postagens de cada blog. + +### BlgPosts + +Esta tabela armazena informações sobre as postagens do blog. Você pode consultar esta tabela para obter postagens de blog por blogs. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [BlgBlogs](#blgblogs) | Id | Para associar a postagem do blog ao blog correspondente. | + +### BlgComments + +Esta tabela armazena informações sobre os comentários feitos nas postagens do blog. Você pode consultar esta tabela para obter comentários por postagens. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [BlgPosts](#blgposts) | Id | Vincula o comentário à postagem do blog correspondente. | +| [BlgComments](#blgcomments) | Id | Vincula o comentário ao comentário pai. | + +### BlgTags + +Esta tabela armazena informações sobre as tags. Quando uma nova tag é usada, um novo registro será adicionado a esta tabela. Você pode consultar esta tabela para obter tags por blogs. + +### BlgPostTags + +Esta tabela é usada para associar tags a postagens de blog, a fim de categorizar e organizar o conteúdo. Você pode consultar esta tabela para obter tags de postagens por postagens. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [BlgTags](#blgtags) | Id | Vincula a tag da postagem à tag correspondente. | +| [BlgPosts](#blgposts) | Id | Vincula a tag da postagem à postagem do blog correspondente. | + +## [Módulo CMS Kit](Cms-Kit/Index.md) + +### CmsUsers + +Esta tabela armazena informações sobre os usuários do módulo CMS Kit. Quando um novo usuário de identidade é criado, um novo registro será adicionado a esta tabela. + +### CmsBlogs + +Esta tabela serve para armazenar informações do blog e separar semanticamente as postagens de cada blog. + +### CmsBlogPosts + +Esta tabela armazena informações sobre as postagens do blog. Você pode consultar esta tabela para obter postagens de blog por blogs. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [CmsUsers](#cmsusers) | Id | Vincula a postagem do blog ao autor correspondente. | + +### CmsBlogFeatures + +Esta tabela armazena informações sobre os recursos do blog. Você pode consultar esta tabela para obter recursos do blog por blogs. + +### CmsComments + +Esta tabela é utilizada pelo sistema de comentários do CMS Kit para armazenar comentários feitos nas postagens do blog. Você pode consultar esta tabela para obter comentários por postagens. + +### CmsTags + +Esta tabela armazena informações sobre as tags. Quando uma nova tag é usada, um novo registro será adicionado a esta tabela. Você pode consultar esta tabela para obter tags por blogs. + +### CmsEntityTags + +Esta tabela é utilizada pelo sistema de gerenciamento de tags para armazenar tags e sua relação com várias entidades, permitindo assim a categorização e organização eficiente do conteúdo. Você pode consultar esta tabela para obter tags de entidades por entidades. + +### CmsGlobalResources + +Esta tabela é uma tabela de banco de dados para o sistema de recursos globais do CMS Kit, permitindo a adição dinâmica de estilos e scripts globais. + +### CmsMediaDescriptors + +Esta tabela é utilizada pelo módulo CMS Kit para gerenciar arquivos de mídia usando o módulo [BlobStoring](../Blob-Storing.md). + +### CmsMenuItems + +Esta tabela é usada pelo sistema de menu do CMS Kit para gerenciar e armazenar informações sobre menus públicos dinâmicos, incluindo detalhes como nomes de exibição de itens de menu, URLs e relacionamentos hierárquicos. + +### CmsPages + +Esta tabela é utilizada pelo sistema de páginas do CMS Kit para armazenar páginas dinâmicas dentro do aplicativo, incluindo informações como URLs de página, títulos e conteúdo. + +### CmsRatings + +Esta tabela é utilizada pelo sistema de classificação do CMS Kit para armazenar classificações feitas em postagens de blog. Você pode consultar esta tabela para obter classificações por postagens. + +### CmsUserReactions + +Esta tabela é utilizada pelo sistema de reações do CMS Kit para armazenar reações feitas em postagens de blog. Você pode consultar esta tabela para obter reações por postagens. + +## [Módulo de Documentação](Docs.md) + +### DocsProjects + +Esta tabela armazena informações do projeto para categorizar documentos de acordo com diferentes projetos. + +### DocsDocuments + +Esta tabela recupera o documento se ele não for encontrado no cache. A documentação está sendo atualizada quando o conteúdo é recuperado do banco de dados. + +### DocsDocumentContributors + +Esta tabela armazena informações sobre os contribuidores dos documentos. Você pode consultar esta tabela para obter contribuidores de documentos por documentos. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [DocsDocuments](#docsdocuments) | Id | Vincula o contribuidor do documento ao documento correspondente. | + +## [Módulo de Gerenciamento de Recursos](Feature-Management.md) + +### AbpFeatureGroups + +Esta tabela armazena informações sobre os grupos de recursos no aplicativo. Por exemplo, você pode agrupar todos os recursos na tabela [`AbpFeatures`](#abpfeatures) relacionados ao módulo `Identity` sob o grupo `Identity`. + +### AbpFeatures + +Esta tabela armazena informações sobre os recursos no aplicativo. Você pode usar a coluna `Name` para vincular cada recurso com seu valor de recurso correspondente na tabela [`AbpFeatureValues`](#abpfeaturevalues), para que você possa gerenciar e organizar facilmente os recursos. + +### AbpFeatureValues + +Esta tabela armazena os valores dos recursos para diferentes provedores. Você pode usar a coluna `Name` para vincular cada valor de recurso com seu recurso correspondente na tabela [`AbpFeatures`](#abpfeatures), para que você possa gerenciar e organizar facilmente os recursos. + +## [Módulo de Identidade](Identity.md) + +### AbpUsers + +Esta tabela armazena informações sobre os usuários de identidade no aplicativo. + +### AbpRoles + +Esta tabela armazena informações sobre os papéis no aplicativo. Os papéis são usados para gerenciar e controlar o acesso a diferentes partes do aplicativo, atribuindo permissões e reivindicações aos papéis e, em seguida, atribuindo esses papéis aos usuários. Esta tabela é importante para gerenciar e organizar os papéis no aplicativo e para definir os direitos de acesso dos usuários. + +### AbpClaimTypes + +Esta tabela armazena informações sobre os tipos de reivindicação usados no aplicativo. Você pode usar as colunas `Name` e `Regex` para filtrar os tipos de reivindicação por nome e padrão regex, respectivamente, para que você possa gerenciar e rastrear facilmente os tipos de reivindicação no aplicativo. + +### AbpLinkUsers + +Esta tabela é útil para vincular várias contas de usuário em diferentes inquilinos ou aplicativos a um único usuário, permitindo que eles alternem facilmente entre suas contas. + +### AbpUserClaims + +Esta tabela pode gerenciar o controle de acesso baseado em usuário, permitindo atribuir reivindicações aos usuários, que descrevem os direitos de acesso do usuário individual. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpUsers](#abpusers) | Id | Vincula a reivindicação do usuário ao usuário correspondente. | + +### AbpUserLogins + +Esta tabela pode armazenar informações sobre os logins externos do usuário, como login com Facebook, Google, etc., e também pode ser usada para rastrear o histórico de login dos usuários. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpUsers](#abpusers) | Id | Vincula o login do usuário ao usuário correspondente. | + +### AbpUserRoles + +Esta tabela pode gerenciar o controle de acesso baseado em usuário, permitindo atribuir papéis aos usuários, que descrevem os direitos de acesso do usuário individual. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpUsers](#abpusers) | Id | Vincula o papel do usuário ao usuário correspondente. | +| [AbpRoles](#abproles) | Id | Vincula o papel do usuário ao papel correspondente. | + +### AbpUserTokens + +Esta tabela pode armazenar informações sobre tokens de atualização, tokens de acesso e outros tokens usados no aplicativo. Também pode ser usado para invalidar ou revogar tokens de usuário. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpUsers](#abpusers) | Id | Vincula o token do usuário ao usuário correspondente. | + +### AbpOrganizationUnits + +Esta tabela é útil para criar e gerenciar uma estrutura hierárquica da organização, permitindo agrupar usuários e atribuir papéis com base na estrutura da organização. Você pode usar as colunas `Code` e `ParentId` para filtrar as unidades organizacionais por código e ID do pai, respectivamente, para que você possa gerenciar e rastrear facilmente as unidades organizacionais no aplicativo. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpOrganizationUnits](#abporganizationunits) | ParentId | Vincula a unidade organizacional à sua unidade organizacional pai. | + +### AbpOrganizationUnitRoles + +Esta tabela é útil para gerenciar o controle de acesso baseado em função no nível das unidades organizacionais, permitindo atribuir diferentes papéis a diferentes partes da estrutura da organização. Você pode usar as colunas `OrganizationUnitId` e `RoleId` para filtrar os papéis por ID da unidade organizacional e ID do papel, respectivamente, para que você possa gerenciar e rastrear facilmente os papéis atribuídos às unidades organizacionais no aplicativo. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpOrganizationUnits](#abporganizationunits) | Id | Vincula o papel da unidade organizacional à unidade organizacional correspondente. | +| [AbpRoles](#abproles) | Id | Vincula o papel da unidade organizacional ao papel correspondente. | + +### AbpUserOrganizationUnits + +Esta tabela armazena informações sobre as unidades organizacionais atribuídas aos usuários no aplicativo. Esta tabela pode gerenciar relacionamentos entre usuário e unidade organizacional e agrupar usuários com base na estrutura da organização. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpUsers](#abpusers) | Id | Vincula a unidade organizacional do usuário ao usuário correspondente. | +| [AbpOrganizationUnits](#abporganizationunits) | Id | Vincula a unidade organizacional do usuário à unidade organizacional correspondente. | + +### AbpRoleClaims + +Esta tabela é útil para gerenciar o controle de acesso baseado em função, permitindo atribuir reivindicações aos papéis, que descrevem os direitos de acesso dos usuários que pertencem a esse papel. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpRoles](#abproles) | Id | Vincula a reivindicação do papel ao papel correspondente. | + +### AbpSecurityLogs + +Esta tabela registra operações e alterações importantes relacionadas às contas de usuário, permitindo que os usuários salvem os logs de segurança para referência futura. + +## [Gerenciamento de Permissões](Permission-Management.md) + +### AbpPermissionGroups + +Esta tabela é importante para gerenciar e organizar as permissões no aplicativo, agrupando-as em categorias lógicas. + +### AbpPermissions + +Esta tabela é importante para gerenciar e controlar o acesso a diferentes partes do aplicativo e para definir as permissões granulares que compõem as permissões ou papéis maiores. + +### AbpPermissionGrants + +A tabela armazena e gerencia as permissões no aplicativo e mantém o controle das permissões concedidas, para quem e quando. Colunas como `Name`, `ProviderName`, `ProviderKey`, `TenantId` podem ser usadas para filtrar as permissões concedidas por nome, nome do provedor, chave do provedor e ID do inquilino, respectivamente, para que você possa gerenciar e rastrear facilmente as permissões concedidas no aplicativo. + +## [Gerenciamento de Configurações](Setting-Management.md) + +### AbpSettings + +Esta tabela armazena pares de chave-valor de configurações para o aplicativo e permite a configuração dinâmica do aplicativo sem a necessidade de recompilação. + +## [OpenIddict](OpenIddict.md) + +### OpenIddictApplications + +Esta tabela pode armazenar informações sobre as aplicações OpenID Connect, incluindo o ID do cliente, segredo do cliente, URI de redirecionamento e outras informações relevantes. Também pode ser usado para autenticar e autorizar clientes usando o protocolo OpenID Connect. + +### OpenIddictAuthorizations + +Esta tabela armazena os dados de autorização do OpenID Connect no aplicativo. Também pode ser usado para gerenciar e validar as concessões de autorização emitidas para clientes e usuários. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [OpenIddictApplications](#openiddictapplications) | Id | Vincula a autorização à aplicação correspondente. | + +### OpenIddictTokens + +Esta tabela pode armazenar informações sobre os tokens OpenID Connect, incluindo o payload do token, expiração, tipo e outras informações relevantes. Também pode ser usado para gerenciar e validar os tokens emitidos para clientes e usuários, como tokens de acesso e tokens de atualização, e controlar o acesso a recursos protegidos. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [OpenIddictApplications](#openiddictapplications) | Id | Vincula o token à aplicação correspondente. | +| [OpenIddictAuthorizations](#openiddictauthorizations) | Id | Vincula o token à autorização correspondente. | + +### OpenIddictScopes + +Esta tabela pode armazenar informações sobre os escopos OpenID Connect, incluindo o nome e a descrição do escopo. Também pode ser usado para definir as permissões ou direitos de acesso associados aos escopos, que são então usados para controlar o acesso a recursos protegidos. + +## [IdentityServer](IdentityServer.md) + +### IdentityServerApiResources + +Esta tabela pode armazenar informações sobre os recursos da API, incluindo o nome do recurso, nome de exibição, descrição e outras informações relevantes. Também pode ser usado para definir os escopos, reivindicações e propriedades associadas aos recursos da API, que são então usados para controlar o acesso a recursos protegidos. + +### IdentityServerIdentityResources + +Esta tabela pode armazenar informações sobre os recursos de identidade, incluindo o nome, nome de exibição, descrição e status habilitado. + +### IdentityServerClients + +Esta tabela pode armazenar informações sobre os clientes, incluindo o ID do cliente, nome do cliente, URI do cliente e outras informações relevantes. Também pode ser usado para definir os escopos, reivindicações e propriedades associadas aos clientes, que são então usados para controlar o acesso a recursos protegidos. + +### IdentityServerApiScopes + +Esta tabela pode armazenar informações sobre os escopos da API, incluindo o nome do escopo, nome de exibição, descrição e outras informações relevantes. Também pode ser usado para definir as reivindicações e propriedades associadas aos escopos da API, que são então usados para controlar o acesso a recursos protegidos. + +### IdentityServerApiResourceClaims + +Esta tabela pode armazenar informações sobre as reivindicações de um recurso da API, incluindo o tipo de reivindicação e o ID do recurso da API. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerApiResources](#identityserverapiresources) | Id | Vincula a reivindicação ao recurso da API correspondente. | + +### IdentityServerIdentityResourceClaims + +Esta tabela pode armazenar informações sobre as reivindicações de um recurso de identidade, incluindo o tipo de reivindicação e o ID do recurso de identidade. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerIdentityResources](#identityserveridentityresources) | Id | Vincula a reivindicação ao recurso de identidade correspondente. | + +### IdentityServerClientClaims + +Esta tabela pode armazenar informações sobre as reivindicações de um cliente, incluindo o tipo de reivindicação, valor da reivindicação e ID do cliente. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula a reivindicação ao cliente correspondente. | + +### IdentityServerApiScopeClaims + +Esta tabela pode armazenar informações sobre as reivindicações de um escopo da API, incluindo o tipo de reivindicação e o ID do escopo da API. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerApiScopes](#identityserverapiscopes) | Id | Vincula a reivindicação ao escopo da API correspondente. | + +### IdentityServerApiResourceProperties + +Esta tabela pode armazenar informações sobre propriedades, incluindo a chave da propriedade e o valor, e o recurso da API associado. Essas propriedades podem armazenar metadados adicionais ou informações de configuração relacionadas aos recursos da API. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerApiResources](#identityserverapiresources) | Id | Vincula a propriedade ao recurso da API correspondente. | + +### IdentityServerIdentityResourceProperties + +Esta tabela pode armazenar informações sobre propriedades, incluindo a chave da propriedade e o valor, e o recurso de identidade associado. Essas propriedades podem armazenar metadados adicionais ou informações de configuração relacionadas aos recursos de identidade. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerIdentityResources](#identityserveridentityresources) | Id | Vincula a propriedade ao recurso de identidade correspondente. | + +### IdentityServerClientProperties + +Esta tabela pode armazenar informações sobre as propriedades de um cliente, incluindo a chave, valor e ID do cliente. Essas propriedades podem armazenar metadados adicionais ou informações de configuração relacionadas aos clientes. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula a propriedade ao cliente correspondente. | + +### IdentityServerApiScopeProperties + +Esta tabela pode armazenar informações sobre as propriedades de um escopo da API, incluindo a chave, valor e ID do escopo da API. Essas propriedades podem armazenar metadados adicionais ou informações de configuração relacionadas aos escopos da API. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerApiScopes](#identityserverapiscopes) | Id | Vincula a propriedade ao escopo da API correspondente. | + +### IdentityServerApiResourceScopes + +Esta tabela pode armazenar informações sobre os escopos de um recurso da API, incluindo o nome do escopo e o ID do recurso da API. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerApiResources](#identityserverapiresources) | Id | Vincula o escopo ao recurso da API correspondente. | + +### IdentityServerClientScopes + +Esta tabela pode armazenar informações sobre os escopos de um cliente, incluindo o escopo e o ID do cliente. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula o escopo ao cliente correspondente. | + +### IdentityServerApiResourceSecrets + +Esta tabela pode armazenar informações sobre os segredos de um recurso da API, incluindo o valor do segredo, data de expiração e ID do recurso da API. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerApiResources](#identityserverapiresources) | Id | Vincula o segredo ao recurso da API correspondente. | + +### IdentityServerClientSecrets + +Esta tabela pode armazenar informações sobre os segredos de um cliente, incluindo o valor do segredo, data de expiração e ID do cliente. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula o segredo ao cliente correspondente. | + +### IdentityServerClientCorsOrigins + +Esta tabela pode armazenar informações sobre as origens CORS de um cliente, incluindo a origem e o ID do cliente. Também pode ser usado para gerenciar e validar as origens CORS de um cliente. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula a origem CORS ao cliente correspondente. | + +### IdentityServerClientGrantTypes + +Esta tabela pode armazenar informações sobre os tipos de concessão de um cliente, incluindo o tipo de concessão e o ID do cliente. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula o tipo de concessão ao cliente correspondente. | + +### IdentityServerClientIdPRestrictions + +Esta tabela pode armazenar informações sobre as restrições do provedor de identidade de um cliente, incluindo o provedor de identidade e o ID do cliente. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula a restrição do provedor de identidade ao cliente correspondente. | + +### IdentityServerClientPostLogoutRedirectUris + +Esta tabela pode armazenar informações sobre os URIs de redirecionamento pós logout de um cliente, incluindo o URI de redirecionamento pós logout e o ID do cliente. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula o URI de redirecionamento pós logout ao cliente correspondente. | + +### IdentityServerClientRedirectUris + +Esta tabela pode armazenar informações sobre os URIs de redirecionamento de um cliente, incluindo o URI de redirecionamento e o ID do cliente. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [IdentityServerClients](#identityserverclients) | Id | Vincula o URI de redirecionamento ao cliente correspondente. | + +### IdentityServerDeviceFlowCodes + +Esta tabela pode armazenar informações sobre os códigos de fluxo de dispositivo, incluindo o código do usuário, código do dispositivo, ID do assunto, ID do cliente, data de criação, expiração, dados e ID da sessão. + +### IdentityServerPersistedGrants + +Esta tabela pode armazenar informações sobre as concessões persistidas, incluindo a chave, tipo, ID do assunto, ID do cliente, data de criação, expiração e dados. + +## Outros + +### AbpBlobContainers + +Esta tabela é importante para fornecer uma melhor experiência do usuário, permitindo que o aplicativo suporte vários contêineres e forneça recursos específicos de BLOB. + +### AbpBlobs + +Esta tabela armazena os dados binários de BLOBs (objetos binários grandes) no aplicativo. Cada BLOB está relacionado a um contêiner na tabela [AbpBlobContainers](#abpblobcontainers), onde o nome do contêiner, ID do inquilino e outras propriedades do contêiner podem ser encontrados. + +#### Chaves Estrangeiras + +| Tabela | Coluna | Descrição | +| --- | --- | --- | +| [AbpBlobContainers](#abpblobcontainers) | Id | Vincula o BLOB ao contêiner correspondente. | + +### AbpLocalizationResources + +Esta tabela armazena os recursos de localização para o aplicativo. Esta tabela é importante para fornecer uma melhor experiência do usuário, permitindo que o aplicativo suporte vários recursos e forneça texto localizado e outros recursos específicos de localização. + +### AbpLocalizationTexts + +A tabela contém o nome do recurso, nome da cultura e um valor codificado em JSON que contém o par chave-valor do texto de localização. Ele permite o armazenamento e gerenciamento eficiente de textos de localização e permite a atualização fácil ou adição de novas traduções para recursos e culturas específicas. \ No newline at end of file diff --git a/docs/pt-BR/Modules/Docs.md b/docs/pt-BR/Modules/Docs.md new file mode 100644 index 0000000000..32a8093ede --- /dev/null +++ b/docs/pt-BR/Modules/Docs.md @@ -0,0 +1,675 @@ +# Módulo de Documentação + +## O que é o Módulo de Documentação? + +O módulo de documentação é um módulo de aplicação para o framework ABP. Ele simplifica a documentação de software. Este módulo é gratuito e de código aberto. + +### Integração + +Atualmente, o módulo de documentação permite que você armazene sua documentação tanto no GitHub quanto no sistema de arquivos. + +### Hospedagem + +O módulo de documentação é um módulo de aplicação e não oferece nenhuma solução de hospedagem. Você pode hospedar sua documentação localmente ou na nuvem. + +### Versionamento + +Quando você usa o GitHub para armazenar sua documentação, o módulo de documentação suporta o versionamento. Se você tiver várias versões para sua documentação, haverá uma caixa de seleção na interface do usuário para alternar entre as versões. Se você escolher o sistema de arquivos para armazenar sua documentação, ele não suportará várias versões. + +[Os documentos](https://docs.abp.io/) para o framework ABP também estão usando este módulo. + +> O módulo de documentação segue as melhores práticas de arquitetura de módulos. + +## Instalação + +Este documento aborda o provedor `Entity Framework Core`, mas você também pode selecionar o `MongoDB` como seu provedor de banco de dados. + +### 1- Criando uma aplicação + +Se você não tiver um projeto ABP existente, você pode gerar um comando CLI a partir da página de início do site abp.io e executá-lo ou executar o comando abaixo: + +```bash +abp new Acme.MyProject +``` + +### 2- Executando a aplicação vazia + +Após baixar o projeto, extraia o arquivo ZIP e abra `Acme.MyProject.sln`. Você verá que a solução consiste nos projetos `Application`, `Application.Contracts`, `DbMigrator`, `Domain`, `Domain.Shared`, `EntityFrameworkCore`, `HttpApi`, `HttpApi.Client` e `Web`. Clique com o botão direito no projeto `Acme.MyProject.Web` e selecione **Definir como Projeto de Inicialização**. + +![Criar um novo projeto](../images/docs-module_solution-explorer.png) + +A string de conexão do banco de dados está localizada em `appsettings.json` do seu projeto `Acme.MyProject.Web`. Se você tiver uma configuração de banco de dados diferente, altere a string de conexão. + +```json +{ + "ConnectionStrings": { + "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProject;Trusted_Connection=True" + } +} +``` + +Execute o projeto `Acme.MyProject.DbMigrator`, ele será responsável por aplicar a migração do banco de dados e os dados iniciais. O banco de dados `MyProject` será criado em seu servidor de banco de dados. + +Agora um projeto ABP vazio foi criado! Agora você pode executar seu projeto e ver o site vazio. + +Para fazer login no seu site, digite `admin` como nome de usuário e `1q2w3E*` como senha. + +### 3- Instalação do Módulo + +Os pacotes do módulo de documentação estão hospedados no NuGet. Existem 4 pacotes que precisam ser instalados em sua aplicação. Cada pacote deve ser instalado no projeto relevante. + +#### 3.1- Usando o ABP CLI + +É recomendado usar o ABP CLI para instalar o módulo. Abra a janela do CMD no diretório do arquivo de solução (`.sln`) e execute o seguinte comando: + +```bash +abp add-module Volo.Docs +``` + +#### 3.2- Instalação manual + +Ou você também pode instalar manualmente o pacote NuGet em cada projeto: + +* Instale o pacote NuGet [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) no projeto `Acme.MyProject.Domain`. + + ```bash + Install-Package Volo.Docs.Domain + ``` + +* Instale o pacote NuGet [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) no projeto `Acme.MyProject.EntityFrameworkCore`. + + ```bash + Install-Package Volo.Docs.EntityFrameworkCore + ``` + +* Instale o pacote NuGet [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) no projeto `Acme.MyProject.Application`. + + ```bash + Install-Package Volo.Docs.Application + ``` + +* Instale o pacote NuGet [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) no projeto `Acme.MyProject.Web`. + + ```bash + Install-Package Volo.Docs.Web + ``` + +##### 3.2.1- Adicionando Dependências de Módulo + +Um módulo ABP deve declarar o atributo `[DependsOn]` se tiver uma dependência de outro módulo. Cada módulo deve ser adicionado no atributo `[DependsOn]` do projeto relevante. + +* Abra `MyProjectDomainModule.cs` e adicione `typeof(DocsDomainModule)` como mostrado abaixo; + + ```csharp + [DependsOn( + typeof(DocsDomainModule), + typeof(AbpIdentityDomainModule), + typeof(AbpAuditingModule), + typeof(BackgroundJobsDomainModule), + typeof(AbpAuditLoggingDomainModule) + )] + public class MyProjectDomainModule : AbpModule + { + //... + } + ``` + +* Abra `MyProjectEntityFrameworkCoreModule.cs` e adicione `typeof(DocsEntityFrameworkCoreModule)` como mostrado abaixo; + + ```csharp + [DependsOn( + typeof(DocsEntityFrameworkCoreModule), + typeof(MyProjectDomainModule), + typeof(AbpIdentityEntityFrameworkCoreModule), + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqlServerModule), + typeof(BackgroundJobsEntityFrameworkCoreModule), + typeof(AbpAuditLoggingEntityFrameworkCoreModule) + )] + public class MyProjectEntityFrameworkCoreModule : AbpModule + { + //... + } + ``` + +* Abra `MyProjectApplicationModule.cs` e adicione `typeof(DocsApplicationModule)` como mostrado abaixo; + + ```csharp + [DependsOn( + typeof(DocsApplicationModule), + typeof(MyProjectDomainModule), + typeof(AbpIdentityApplicationModule))] + public class MyProjectApplicationModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure<AbpPermissionOptions>(options => + { + options.DefinitionProviders.Add<MyProjectPermissionDefinitionProvider>(); + }); + + Configure<AbpAutoMapperOptions>(options => + { + options.AddProfile<MyProjectApplicationAutoMapperProfile>(); + }); + } + } + ``` + +* Abra `MyProjectWebModule.cs` e adicione `typeof(DocsWebModule)` como mostrado abaixo; + + ```csharp + [DependsOn( + typeof(DocsWebModule), + typeof(MyProjectApplicationModule), + typeof(MyProjectEntityFrameworkCoreModule), + typeof(AbpAutofacModule), + typeof(AbpIdentityWebModule), + typeof(AbpAccountWebModule), + typeof(AbpAspNetCoreMvcUiBasicThemeModule) + )] + public class MyProjectWebModule : AbpModule + { + //... + } + ``` + +##### 3.2.2- Adicionando Pacote NPM + +Abra `package.json` e adicione `@abp/docs": "^5.0.0` como mostrado abaixo: + + ```json + { + "version": "1.0.0", + "name": "my-app", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^5.0.0", + "@abp/docs": "^5.0.0" + } + } + ``` + +Em seguida, abra o terminal de linha de comando na pasta do projeto `Acme.MyProject.Web` e execute o seguinte comando: + +````bash +abp install-libs +```` + +### 4- Integração com o Banco de Dados + +#### 4.1- Integração com o Entity Framework + +Se você escolher o Entity Framework como seu provedor de banco de dados, precisará configurar o módulo de documentação. Para fazer isso; + +- Abra `MyProjectMigrationsDbContext.cs` e adicione `builder.ConfigureDocs()` ao `OnModelCreating()`. + + ```csharp + public class MyProjectMigrationsDbContext : AbpDbContext<MyProjectMigrationsDbContext> + { + public MyProjectMigrationsDbContext(DbContextOptions<MyProjectMigrationsDbContext> options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + /* Inclua módulos no contexto de migração do banco de dados */ + + builder.ConfigurePermissionManagement(); + builder.ConfigureSettingManagement(); + builder.ConfigureBackgroundJobs(); + builder.ConfigureAuditLogging(); + builder.ConfigureIdentity(); + builder.ConfigureIdentityServer(); + builder.ConfigureFeatureManagement(); + builder.ConfigureTenantManagement(); + builder.ConfigureDocs(); //Adicione esta linha para configurar o módulo de documentação + + /* Configure personalizações para entidades dos módulos incluídos */ + + builder.Entity<IdentityUser>(b => + { + b.ConfigureCustomUserProperties(); + }); + + /* Configure suas próprias tabelas/entidades dentro do método ConfigureMyProject */ + + builder.ConfigureMyProject(); + } + } + ``` + +* Abra o `Console do Gerenciador de Pacotes` no `Visual Studio` e escolha `Acme.MyProject.EntityFrameworkCore` como projeto padrão. Em seguida, escreva o comando abaixo para adicionar a migração para o módulo de documentação. + + ```csharp + add-migration Added_Docs_Module + ``` + + Quando o comando for executado com sucesso, você verá um novo arquivo de migração chamado `20181221111621_Added_Docs_Module` na pasta `Acme.MyProject.EntityFrameworkCore\Migrations`. + + Agora, atualize o banco de dados para as alterações do módulo de documentação. Para fazer isso, execute o código abaixo no `Console do Gerenciador de Pacotes` no `Visual Studio`. Certifique-se de que `Acme.MyProject.EntityFrameworkCore` ainda é o projeto padrão. + + ```csharp + update-database + ``` + + Por fim, você pode verificar seu banco de dados para ver as tabelas recém-criadas. Por exemplo, você pode ver a tabela `DocsProjects` adicionada ao seu banco de dados. + +### 5- Vinculando o Módulo de Documentação + +A rota padrão para o módulo de documentação é; + +```txt +/Documents +``` + +Para adicionar o link do módulo de documentação ao menu de sua aplicação; + +* Abra `MyProjectMenuContributor.cs` e adicione a linha abaixo ao método `ConfigureMainMenuAsync()`. + + ```csharp + context.Menu.Items.Add(new ApplicationMenuItem("MyProject.Docs", l["Menu:Docs"], "/Documents")); + ``` + + A aparência final de **MyProjectMenuContributor.cs** + + ```csharp + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<MyProjectResource>>(); + + context.Menu.Items.Insert(0, new ApplicationMenuItem("MyProject.Home", l["Menu:Home"], "/")); + + context.Menu.Items.Add(new ApplicationMenuItem("MyProject.Docs", l["Menu:Docs"], "/Documents")); + } + ``` + +A palavra-chave `Menu:Docs` é uma chave de localização. Para localizar o texto do menu, abra `Localization\MyProject\en.json` no projeto `Acme.MyProject.Domain`. E adicione a linha abaixo + +```json +"Menu:Docs": "Documentos" +``` + +A aparência final de **en.json** + +```json +{ + "culture": "en", + "texts": { + "Menu:Home": "Início", + "Welcome": "Bem-vindo", + "LongWelcomeMessage": "Bem-vindo à aplicação. Este é um projeto inicial baseado no framework ABP. Para obter mais informações, visite abp.io.", + "Menu:Docs": "Documentos" + } +} +``` + +O novo item de menu para o Módulo de Documentação foi adicionado ao menu. Execute sua aplicação web e acesse a URL `http://localhost:YOUR_PORT_NUMBER/documents`. + +Você verá um aviso que diz; + +```txt +Ainda não há projetos! +``` + +Como ainda não adicionamos nenhum projeto, este aviso é normal. + +### 6- Adicionando um Novo Projeto de Documentação + +Abra `DocsProjects` em seu banco de dados e insira um novo registro com as seguintes informações de campo; + +* **Name**: O nome de exibição do nome do documento que será mostrado na página da web. +* **ShortName**: Um nome curto e amigável para URL que será usado na URL de seus documentos. +* **Format**: O formato do documento (para Markdown: `md`, para HTML: `html`) +* **DefaultDocumentName**: O documento para a página inicial. +* **NavigationDocumentName**: O documento a ser usado para o menu de navegação (Índice). +* **MinimumVersion**: A versão mínima para mostrar os documentos. As versões abaixo não serão listadas. +* **DocumentStoreType**: A fonte dos documentos (para GitHub:`GitHub`, para sistema de arquivos`FileSystem`) +* **ExtraProperties**: Um JSON serializado que armazena configurações especiais para o `DocumentStoreType` selecionado. +* **MainWebsiteUrl**: A URL para quando o usuário clicar no logotipo da página do módulo de documentação. Você pode simplesmente definir como `/` para vincular ao endereço raiz do seu site. +* **LatestVersionBranchName**: Esta é uma configuração para o GitHub. É o nome do branch que será usado para recuperar os documentos. Você pode definir como `master`. + +#### Exemplo de Registro de Projeto para "GitHub" + +Você pode usar a documentação do [ABP Framework](https://github.com/abpframework/abp/) no GitHub para configurar seu repositório de documentos do GitHub. + +- Name: `ABP framework (GitHub)` + +- ShortName: `abp` + +- Format: `md` + +- DefaultDocumentName: `Index` + +- NavigationDocumentName: `docs-nav.json` + +- MinimumVersion: `<NULL>` (sem versão mínima) + +- DocumentStoreType: `GitHub` + +- ExtraProperties: + + ```json + {"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***","GitHubUserAgent":""} + ``` + + Observe que `GitHubAccessToken` está mascarado com `***`. É um token privado que você deve obter do GitHub. Consulte https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ + +- MainWebsiteUrl: `/` + +- LatestVersionBranchName: `dev` + +Para bancos de dados `SQL`, você pode usar o comando `T-SQL` abaixo para inserir o exemplo especificado em sua tabela `DocsProjects`: + +```mssql +INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName], [ConcurrencyStamp]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939659', N'ABP framework (FileSystem)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'FileSystem', N'{"Path":"C:\\Github\\abp\\docs"}', N'/', NULL, N'', N'12f21123e08e4f15bedbae0b2d939659') +``` + +Esteja ciente de que `GitHubAccessToken` está mascarado. É um token privado e você deve obter seu próprio token e substituir a string `***`. + +Agora você pode executar a aplicação e navegar até `/Documents`. + +#### Exemplo de Registro de Projeto para "FileSystem" + +Você pode usar a documentação do [ABP Framework](https://github.com/abpframework/abp/) no GitHub para configurar seu repositório de documentos do GitHub. + +- Name: `ABP framework (FileSystem)` + +- ShortName: `abp` + +- Format: `md` + +- DefaultDocumentName: `Index` + +- NavigationDocumentName: `docs-nav.json` + +- MinimumVersion: `<NULL>` (sem versão mínima) + +- DocumentStoreType: `FileSystem` + +- ExtraProperties: + + ```json + {"Path":"C:\\Github\\abp\\docs"} + ``` + + Observe que `Path` deve ser substituído pelo diretório de documentos local. Você pode obter a documentação do ABP Framework em https://github.com/abpframework/abp/tree/master/docs e copiar para o diretório `C:\\Github\\abp\\docs` para que funcione. + +- MainWebsiteUrl: `/` + +- LatestVersionBranchName: `<NULL>` + +Para bancos de dados `SQL`, você pode usar o comando `T-SQL` abaixo para inserir o exemplo especificado em sua tabela `DocsProjects`: + +```mssql +INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939659', N'ABP framework (FileSystem)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'FileSystem', N'{"Path":"C:\\Github\\abp\\docs"}', N'/', NULL, N'') +``` + +Adicione um dos projetos de exemplo acima e execute a aplicação. No menu, você verá o link `Documentos`, clique no link do menu para abrir a página de documentos. + +Até agora, criamos um novo aplicativo a partir do site abp.io e o preparamos para o módulo de documentação. + +### 7- Criando um Novo Documento + +No exemplo de registros de projeto, você vê que o `Format` é especificado como `md`, que se refere ao [Mark Down](https://en.wikipedia.org/wiki/Markdown). Você pode ver o guia de referência do mark down seguindo o link abaixo; + +https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet + +O módulo de documentação do ABP pode renderizar mark down para HTML. + +Agora vamos dar uma olhada em um exemplo de documento em formato mark down. + +~~~markdown +# Este é um cabeçalho + +Bem-vindo ao Módulo de Documentação. + +## Este é um subcabeçalho + + [Este é um link](https://abp.io) + +![Esta é uma imagem](https://abp.io/assets/my-image.png) + +## Este é um bloco de código + +```csharp +public class Person +{ + public string Name { get; set; } + + public string Address { get; set; } +} +``` +~~~ + +Como exemplo, você pode ver a documentação do ABP Framework: + +[https://github.com/abpframework/abp/blob/master/docs/en/](https://github.com/abpframework/abp/blob/master/docs/en/) + +#### Recurso de seções condicionais (Usando Scriban) + +O módulo de documentação usa o [Scriban](https://github.com/lunet-io/scriban/tree/master/doc) para mostrar ou ocultar algumas partes de um documento de forma condicional. Para usar esse recurso, você deve criar um arquivo JSON como **documento de parâmetros** para cada idioma. Ele conterá todas as chaves-valores, bem como seus nomes de exibição. + +Por exemplo, [en/docs-params.json](https://github.com/abpio/abp-commercial-docs/blob/master/en/docs-params.json): + +```json +{ + "parameters": [{ + "name": "UI", + "displayName": "UI", + "values": { + "MVC": "MVC / Razor Pages", + "NG": "Angular" + } + }, + { + "name": "DB", + "displayName": "Database", + "values": { + "EF": "Entity Framework Core", + "Mongo": "MongoDB" + } + }, + { + "name": "Tiered", + "displayName": "Tiered", + "values": { + "No": "Not Tiered", + "Yes": "Tiered" + } + }] +} +``` + +Como nem todos os documentos de seus projetos podem ter seções ou precisar de todos esses parâmetros, você deve declarar quais desses parâmetros serão usados para dividir o documento, como um bloco JSON em qualquer lugar do documento. + +Por exemplo [Getting-Started.md](https://github.com/abpio/abp-commercial-docs/blob/master/en/getting-started.md): + +``` +..... + +​```json +//[doc-params] +{ + "UI": ["MVC","NG"], + "DB": ["EF", "Mongo"], + "Tiered": ["Yes", "No"] +} +​``` + +........ +``` + +Esta seção será excluída automaticamente durante a renderização. E, é claro, essas chaves de valores devem corresponder às do **documento de parâmetros**. + +![Interface](../images/docs-section-ui.png) + +Agora você pode usar a sintaxe do **Scriban** para criar seções em seu documento. + +Por exemplo: + +```` +{{ if UI == "NG" }} + +* O argumento `-u` especifica o framework de IU, `angular` neste caso. + +{{ end }} + +{{ if DB == "Mongo" }} + +* O argumento `-d` especifica o provedor de banco de dados, `mongodb` neste caso. + +{{ end }} + +{{ if Tiered == "Yes" }} + +* O argumento `--tiered` é usado para criar uma solução em camadas em que o servidor de autenticação, as camadas de IU e API são fisicamente separadas. + +{{ end }} + +```` + +Você também pode usar variáveis em um texto, adicionando o sufixo **_Value** à sua chave: + +```txt +Este documento pressupõe que você prefere usar **{{ UI_Value }}** como o framework de IU e **{{ DB_Value }}** como o provedor de banco de dados. +``` + +Além disso, as chaves **Document_Language_Code** e **Document_Version** são predefinidas se você quiser obter o código do idioma ou a versão do documento atual (isso pode ser útil para criar links que redirecionam para outro sistema de documentação em outro domínio). + +------ + +**AVISO IMPORTANTE**: O Scriban usa "{{" e "}}" para a sintaxe. Portanto, você deve usar blocos de escape se for usá-los em seu documento (um documento Angular, por exemplo). Consulte a [documentação do Scriban](https://github.com/lunet-io/scriban/blob/master/doc/language.md#13-escape-block) para obter mais informações. + +### 8- Criando o Documento de Navegação + +O documento de navegação é o menu principal da página de documentos. Ele está localizado no lado esquerdo da página. É um arquivo `JSON`. Dê uma olhada no exemplo de documento de navegação abaixo para entender a estrutura. + +```json +{ + "items":[ + { + "text":"Item de Menu de Exemplo - 1", + "items":[ + { + "text":"Item de Menu de Exemplo - 1.1", + "items":[ + { + "text":"Item de Menu de Exemplo - 1.1.1", + "path":"SampleMenuItem_1_1_1.md" + } + ] + }, + { + "text":"Item de Menu de Exemplo - 1.2", + "items":[ + { + "text":"Item de Menu de Exemplo - 1.2.1", + "path":"SampleMenuItem_1_2_1.md" + }, + { + "text":"Item de Menu de Exemplo - 1.2.2", + "path":"SampleMenuItem_1_2_2.md" + } + ] + } + ] + }, + { + "text":"Item de Menu de Exemplo - 2", + "items":[ + { + "text":"Item de Menu de Exemplo - 2.1", + "items":[ + { + "text":"Item de Menu de Exemplo - 2.1.1", + "path":"SampleMenuItem_2_1_1.md" + } + ] + } + ] + } + ] +} +``` + +O exemplo acima de arquivo `JSON` renderiza o menu de navegação abaixo como `HTML`. + +![Menu de navegação](../images/docs-module_download-sample-navigation-menu.png) + +Finalmente, um novo Módulo de Documentação é adicionado ao seu projeto, que é alimentado pelo GitHub. + + +## Pesquisa de Texto Completo (Elastic Search) + +O módulo de documentação suporta pesquisa de texto completo usando o Elastic Search. Ele não está habilitado por padrão. Você pode configurar `DocsElasticSearchOptions` para habilitá-lo. + +```csharp +Configure<DocsElasticSearchOptions>(options => +{ + options.Enable = true; + options.IndexName = "your_index_name"; //o nome do índice padrão é abp_documents +}); +``` + +O `Índice` é criado automaticamente após o início da aplicação se o `Índice` não existir. + +`DefaultElasticClientProvider` é responsável por criar `IElasticClient`. Por padrão, ele lê a `Url` do Elastic Search da `IConfiguration`. +Se o seu `IElasticClient` precisar de configurações adicionais, use a substituição do serviço `IElasticClientProvider` e substitua-o no sistema de [injeção de dependência](../Dependency-Injection.md). + +```json +{ + "ElasticSearch": { + "Url": "http://localhost:9200" + } +} +``` + + +## Destacando Linhas + +Você pode aplicar destaque a linhas de código específicas ou a um intervalo de linhas sequenciais. +Veja os exemplos a seguir: + +``` + ```C# {3, 5} + public class Book : Entity<Guid> + { + public string Name { get; set; } + public string Surname { get; set; } + } + ``` +``` + +``` + ```C# {2-4} + public class Book : Entity<Guid> + { + public string Name { get; set; } + public string Surname { get; set; } + } + ``` +``` + +``` + ```C# {1, 2-4} + public class Book : Entity<Guid> + { + public string Name { get; set; } + public string Surname { get; set; } + } + ``` +``` + +--- + + + +## Próximo + +O Módulo de Documentação também está disponível como um aplicativo independente. Confira [VoloDocs](../Apps/VoloDocs). \ No newline at end of file diff --git a/docs/pt-BR/Modules/Feature-Management.md b/docs/pt-BR/Modules/Feature-Management.md new file mode 100644 index 0000000000..1928b09a77 --- /dev/null +++ b/docs/pt-BR/Modules/Feature-Management.md @@ -0,0 +1,105 @@ +# Módulo de Gerenciamento de Recursos + +O módulo de Gerenciamento de Recursos implementa a interface `IFeatureManagementStore` definida pelo [Sistema de Recursos](../Features.md). + +> Este documento aborda apenas o módulo de gerenciamento de recursos que persiste os valores dos recursos em um banco de dados. Consulte o documento [recursos](../Features.md) para obter mais informações sobre o sistema de recursos. + +## Como Instalar + +Este módulo vem pré-instalado (como pacotes NuGet/NPM). Você pode continuar a usá-lo como pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` [CLI](../CLI.md)) para desenvolver seu próprio módulo personalizado. + +### O Código Fonte + +O código-fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/feature-management). O código-fonte é licenciado com [MIT](https://choosealicense.com/licenses/mit/), portanto, você pode usá-lo e personalizá-lo livremente. + +## Interface do Usuário + +### Diálogo de Gerenciamento de Recursos + +O módulo de gerenciamento de recursos fornece um diálogo reutilizável para gerenciar recursos relacionados a um objeto. Por exemplo, o [Módulo de Gerenciamento de Inquilinos](Tenant-Management.md) o utiliza para gerenciar os recursos dos inquilinos na página de Gerenciamento de Inquilinos. + +![features-module-opening](../images/features-module-opening.png) + +Quando você clica em *Ações* -> *Recursos* para um inquilino, o diálogo de gerenciamento de recursos é aberto. Uma captura de tela de exemplo deste diálogo com dois recursos definidos: + +![features-modal](../images/features-modal.png) + +Neste diálogo, você pode habilitar, desabilitar ou definir valores para os recursos de um inquilino. + +## IFeatureManager + +`IFeatureManager` é o serviço principal fornecido por este módulo. Ele é usado para ler e alterar os valores de configuração para os inquilinos em um aplicativo multi-inquilino. `IFeatureManager` é normalmente usado pelo *Diálogo de Gerenciamento de Recursos*. No entanto, você pode injetá-lo se precisar definir um valor de recurso. + +> Se você apenas deseja ler os valores dos recursos, use o `IFeatureChecker` conforme explicado no documento [Recursos](../Features.md). + +**Exemplo: Obter/definir o valor de um recurso para um inquilino** + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.FeatureManagement; + +namespace Demo +{ + public class MyService : ITransientDependency + { + private readonly IFeatureManager _featureManager; + + public MyService(IFeatureManager featureManager) + { + _featureManager = featureManager; + } + + public async Task SetFeatureDemoAsync(Guid tenantId, string value) + { + await _featureManager + .SetForTenantAsync(tenantId, "Recurso1", value); + + var currentValue = await _featureManager + .GetOrNullForTenantAsync("Recurso1", tenantId); + } + } +} +```` + +## Provedores de Gerenciamento de Recursos + +O Módulo de Gerenciamento de Recursos é extensível, assim como o [sistema de recursos](../Features.md). Você pode estendê-lo definindo provedores de gerenciamento de recursos. Existem 3 provedores de gerenciamento de recursos pré-construídos registrados na seguinte ordem: + +* `DefaultValueFeatureManagementProvider`: Obtém o valor do valor padrão da definição do recurso. Ele não pode definir o valor padrão, pois os valores padrão são codificados na definição do recurso. +* `EditionFeatureManagementProvider`: Obtém ou define os valores dos recursos para uma edição. A edição é um grupo de recursos atribuídos a inquilinos. O sistema de edição não foi implementado pelo módulo de Gerenciamento de Inquilinos. Você pode implementá-lo por conta própria ou adquirir o [Módulo SaaS](https://commercial.abp.io/modules/Volo.Saas) do ABP Commercial, que o implementa e também fornece mais recursos SaaS, como assinatura e pagamento. +* `TenantFeatureManagementProvider`: Obtém ou define os valores dos recursos para inquilinos. + +`IFeatureManager` usa esses provedores nos métodos de obtenção/definição. Normalmente, cada provedor de gerenciamento de recursos define métodos de extensão no serviço `IFeatureManager` (como `SetForTenantAsync` definido pelo provedor de gerenciamento de recursos de inquilinos). + +Se você deseja criar seu próprio provedor, implemente a interface `IFeatureManagementProvider` ou herde da classe base `FeatureManagementProvider`: + +````csharp +public class CustomFeatureProvider : FeatureManagementProvider +{ + public override string Name => "Custom"; + + public CustomFeatureProvider(IFeatureManagementStore store) + : base(store) + { + } +} +```` + +A classe base `FeatureManagementProvider` faz a implementação padrão (usando o `IFeatureManagementStore`) para você. Você pode substituir os métodos base conforme necessário. Todo provedor deve ter um nome exclusivo, que é `Custom` neste exemplo (mantenha-o curto, pois ele é salvo no banco de dados para cada registro de valor de recurso). + +Depois de criar sua classe de provedor, você deve registrá-la usando a classe de opções `FeatureManagementOptions` [options class](../Options.md): + +````csharp +Configure<FeatureManagementOptions>(options => +{ + options.Providers.Add<CustomFeatureProvider>(); +}); +```` + +A ordem dos provedores é importante. Os provedores são executados na ordem inversa. Isso significa que o `CustomFeatureProvider` é executado primeiro neste exemplo. Você pode inserir seu provedor em qualquer ordem na lista `Providers`. + +## Veja também + +* [Recursos](../Features.md) \ No newline at end of file diff --git a/docs/pt-BR/Modules/Identity.md b/docs/pt-BR/Modules/Identity.md new file mode 100644 index 0000000000..8c50fee03b --- /dev/null +++ b/docs/pt-BR/Modules/Identity.md @@ -0,0 +1,321 @@ +# Módulo de Gerenciamento de Identidade + +O módulo de identidade é usado para gerenciar funções, usuários e suas permissões, com base na biblioteca de identidade da Microsoft. + +## Como instalar + +Este módulo já vem pré-instalado (como pacotes NuGet/NPM). Você pode continuar a usá-lo como pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` [CLI](../CLI.md)) para desenvolver seu próprio módulo personalizado. + +### O Código-fonte + +O código-fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/identity). O código-fonte é licenciado com [MIT](https://choosealicense.com/licenses/mit/), então você pode usá-lo e personalizá-lo livremente. + +## Interface do Usuário + +Este módulo fornece opções de interface do usuário [Blazor](../UI/Blazor/Overall.md), [Angular](../UI/Angular/Quick-Start.md) e [MVC / Razor Pages](../UI/AspNetCore/Overall.md). + +### Itens do Menu + +Este módulo adiciona um item de menu *Gerenciamento de Identidade* no menu *Administração*: + +![identity-module-menu](../images/identity-module-menu.png) + +Os itens do menu e as páginas relacionadas são autorizados. Isso significa que o usuário atual deve ter as permissões relacionadas para torná-los visíveis. A função `admin` (e os usuários com essa função - como o usuário `admin`) já possui essas permissões. Se você deseja habilitar permissões para outras funções/usuários, abra a caixa de diálogo *Permissões* na página *Funções* ou *Usuários* e marque as permissões conforme mostrado abaixo: + +![identity-module-permissions](../images/identity-module-permissions.png) + +Consulte o documento de [Autorização](../Authorization.md) para entender o sistema de permissões. + +### Páginas + +Esta seção apresenta as principais páginas fornecidas por este módulo. + +#### Usuários + +Esta página é usada para ver a lista de usuários. Você pode criar/editar e excluir usuários, atribuir usuários a funções. + +![identity-module-users](../images/identity-module-users.png) + +Um usuário pode ter zero ou mais funções. Os usuários herdam permissões de suas funções. Além disso, você pode atribuir permissões diretamente aos usuários (clicando no botão *Ações*, em seguida, selecionando *Permissões*). + +#### Funções + +As funções são usadas para agrupar permissões e atribuí-las aos usuários. + +![identity-module-roles](../images/identity-module-roles.png) + +Além do nome da função, existem duas propriedades de uma função: + +* `Padrão`: Se uma função for marcada como "padrão", essa função será atribuída aos novos usuários por padrão quando eles se registrarem na aplicação (usando o [Módulo de Conta](Account.md)). +* `Público`: Uma função pública de um usuário pode ser vista por outros usuários na aplicação. Essa funcionalidade não tem uso no módulo de identidade, mas é fornecida como uma funcionalidade que você pode querer usar em sua própria aplicação. + +## Outras Funcionalidades + +Esta seção abrange algumas outras funcionalidades fornecidas por este módulo que não possuem páginas de interface do usuário. + +### Unidades Organizacionais + +As unidades organizacionais (OU) podem ser usadas para agrupar usuários e entidades de forma hierárquica. + +#### Entidade Unidade Organizacional + +Uma OU é representada pela entidade **UnidadeOrganizacional**. As propriedades fundamentais desta entidade são: + +- **TenantId**: Id do locatário desta OU. Pode ser nulo para OUs do host. +- **ParentId**: Id da OU pai. Pode ser nulo se esta for uma OU raiz. +- **Código**: Um código de string hierárquico que é único para um locatário. +- **DisplayName**: Nome exibido da OU. + +#### Árvore de Organização + +Como uma OU pode ter um pai, todas as OUs de um locatário estão em uma estrutura de **árvore**. Existem algumas regras para esta árvore: + +- Pode haver mais de uma raiz (onde o `ParentId` é `null`). +- Há um limite para a contagem de filhos de primeiro nível de uma OU (por causa do comprimento fixo da unidade de código OU explicado abaixo). + +#### Código da OU + +O código da OU é gerado automaticamente e mantido pelo serviço `GerenciadorUnidadeOrganizacional`. É uma string que se parece com isso: + +"**00001.00042.00005**" + +Este código pode ser usado para consultar facilmente o banco de dados para todos os filhos de uma OU (recursivamente). Existem algumas regras para este código (aplicadas automaticamente quando você usa o `GerenciadorUnidadeOrganizacional`): + +- É **único** para um [locatário](../Multi-Tenancy.md). +- Todos os filhos da mesma OU têm códigos que **começam com o código da OU pai**. +- É de **comprimento fixo** e baseado no nível da OU na árvore, conforme mostrado no exemplo. +- Embora o código da OU seja único, ele pode ser **alterado** se você mover a OU relacionada. + +Observe que você deve referenciar uma OU pelo Id, não pelo Código, porque o Código pode ser alterado posteriormente. + +#### Gerenciador de Unidade Organizacional + +A classe `GerenciadorUnidadeOrganizacional` pode ser [injetada](../Dependency-Injection.md) e usada para gerenciar OUs. Casos de uso comuns são: + +- Criar, atualizar ou excluir uma OU +- Mover uma OU na árvore de OUs. +- Obter informações sobre a árvore de OUs e seus itens. + +### Log de Segurança de Identidade + +O sistema de log de segurança registra algumas operações ou alterações importantes em sua conta (como *login* e *alteração de senha*). Você também pode salvar o log de segurança, se necessário. + +Você pode injetar e usar `GerenciadorLogSegurancaIdentidade` ou `IGerenciadorLogSeguranca` para gravar logs de segurança. Ele criará um objeto de log por padrão e preencherá alguns valores comuns, como `CreationTime`, `ClientIpAddress`, `BrowserInfo`, `usuário/locatário atual`, etc. Claro, você pode substituí-los. + +```cs +await GerenciadorLogSegurancaIdentidade.SalvarAsync(new ContextoLogSegurancaIdentidade() +{ + Identidade = "IdentityServer", + Ação = "AlterarSenha" +}); +``` + +Configure `OpcoesLogSegurancaAbp` para fornecer o nome do aplicativo (no caso de você ter várias aplicações e desejar distinguir as aplicações nos logs) para o log ou desativar esse recurso. + +```cs +Configure<OpcoesLogSegurancaAbp>(opcoes => +{ + opcoes.NomeAplicativo = "AbpSecurityTest"; +}); +``` + +## Opções + +`OpcoesIdentidade` é a classe de [opções](../Options.md) padrão fornecida pela biblioteca de [identidade](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) da Microsoft. Portanto, você pode definir essas opções no método `ConfigureServices` da sua classe de [módulo](../Module-Development-Basics.md). + +**Exemplo: Definir o comprimento mínimo necessário das senhas** + +````csharp +Configure<OpcoesIdentidade>(opcoes => +{ + opcoes.Senha.ComprimentoMinimo = 5; +}); +```` + +O ABP leva essas opções um passo adiante e permite que você as altere em tempo de execução usando o [sistema de configurações](../Settings.md). Você pode [injetar](../Dependency-Injection.md) `IGerenciadorConfiguracao` e usar um dos métodos `Set...` para alterar os valores das opções para um usuário, um locatário ou globalmente para todos os usuários. + +**Exemplo: Alterar o comprimento mínimo necessário das senhas para o locatário atual** + +````csharp +public class MeuServico : IDependencyTransient +{ + private readonly IGerenciadorConfiguracao _gerenciadorConfiguracao; + + public MeuServico(IGerenciadorConfiguracao gerenciadorConfiguracao) + { + _gerenciadorConfiguracao = gerenciadorConfiguracao; + } + + public async Task AlterarComprimentoMinSenha(int comprimentoMin) + { + await _gerenciadorConfiguracao.DefinirParaLocatarioAtualAsync( + NomesConfiguracaoIdentidade.Senha.ComprimentoMinimo, + comprimentoMin.ToString() + ); + } +} +```` + +A classe `NomesConfiguracaoIdentidade` (no namespace `Volo.Abp.Identity.Settings`) define constantes para os nomes das configurações. + +## Eventos Distribuídos + +Este módulo define os seguintes ETOs (Event Transfer Objects) para permitir que você se inscreva em alterações nas entidades do módulo; + +* `UserEto` é publicado em alterações feitas em uma entidade `IdentityUser`. +* `IdentityRoleEto` é publicado em alterações feitas em uma entidade `IdentityRole`. +* `IdentityClaimTypeEto` é publicado em alterações feitas em uma entidade `IdentityClaimType`. +* `OrganizationUnitEto` é publicado em alterações feitas em uma entidade `OrganizationUnit`. + +**Exemplo: Ser notificado quando um novo usuário for criado** + +````csharp +public class MeuManipulador : + IManipuladorEventoDistribuido<EntityCreatedEto<UserEto>>, + IDependencyTransient +{ + public async Task ManipularEventoAsync(EntityCreatedEto<UserEto> evento) + { + UserEto user = evento.Entity; + // TODO: ... + } +} +```` + +`UserEto` e `IdentityRoleEto` são configurados para publicar automaticamente os eventos. Você deve configurar você mesmo para os outros. Consulte o documento de [Distributed Event Bus](../Distributed-Event-Bus.md) para aprender detalhes dos eventos pré-definidos. + +> A inscrição nos eventos distribuídos é especialmente útil para cenários distribuídos (como arquitetura de microsserviços). Se você está construindo uma aplicação monolítica ou ouvindo eventos no mesmo processo que executa o Módulo de Identidade, então a inscrição nos [eventos locais](../Local-Event-Bus.md) pode ser mais eficiente e fácil. + +## Internos + +Esta seção abrange alguns detalhes internos do módulo que você não precisa muito, mas pode precisar usar em alguns casos. + +### Camada de Domínio + +#### Agregados + +##### Usuário + +Um usuário é geralmente uma pessoa que faz login e usa a aplicação. + +* `IdentityUser` (raiz do agregado): Representa um usuário no sistema. + * `IdentityUserRole` (coleção): Funções do usuário. + * `IdentityUserClaim` (coleção): Reivindicações personalizadas do usuário. + * `IdentityUserLogin` (coleção): Logins externos do usuário. + * `IdentityUserToken` (coleção): Tokens do usuário (usados pelos serviços de identidade da Microsoft). + +##### Função + +Uma função é tipicamente um grupo de permissões para atribuir aos usuários. + +* `IdentityRole` (raiz do agregado): Representa uma função no sistema. + * `IdentityRoleClaim` (coleção): Reivindicações personalizadas da função. + +##### Tipo de Reivindicação + +Um tipo de reivindicação é uma definição de uma reivindicação personalizada que pode ser atribuída a outras entidades (como funções e usuários) no sistema. + +* `IdentityClaimType` (raiz do agregado): Representa uma definição de tipo de reivindicação. Ele contém algumas propriedades (por exemplo, Obrigatório, Regex, Descrição, ValueType) para definir o tipo de reivindicação e as regras de validação. + +##### Log de Segurança de Identidade + +Um objeto `IdentitySecurityLog` representa uma operação relacionada à autenticação (como *login*) no sistema. + +* `IdentitySecurityLog` (raiz do agregado): Representa um log de segurança no sistema. + +##### Unidade Organizacional + +Uma unidade organizacional é uma entidade em uma estrutura hierárquica. + +* ```OrganizationUnit``` (raiz do agregado): Representa uma unidade organizacional no sistema. + * ```Roles``` (coleção): Funções da unidade organizacional. + +#### Repositórios + +Os seguintes repositórios personalizados são definidos para este módulo: + +* `IIdentityUserRepository` +* `IIdentityRoleRepository` +* `IIdentityClaimTypeRepository` +* ```IIdentitySecurityLogRepository``` +* ```IOrganizationUnitRepository``` + +#### Serviços de Domínio + +##### Gerenciador de Usuário + +`IdentityUserManager` é usado para gerenciar usuários, suas funções, reivindicações, senhas, e-mails, etc. Ele é derivado da classe `UserManager<T>` da Microsoft Identity, onde `T` é `IdentityUser`. + +##### Gerenciador de Função + +`IdentityRoleManager` é usado para gerenciar funções e suas reivindicações. Ele é derivado da classe `RoleManager<T>` da Microsoft Identity, onde `T` é `IdentityRole`. + +##### Gerenciador de Tipo de Reivindicação + +`IdenityClaimTypeManager` é usado para realizar algumas operações para a raiz do agregado `IdentityClaimType`. + +##### Gerenciador de Unidade Organizacional + +```OrganizationUnitManager``` é usado para realizar algumas operações para a raiz do agregado ```OrganizationUnit```. + +##### Gerenciador de Log de Segurança + +```IdentitySecurityLogManager``` é usado para salvar logs de segurança. + +### Camada de Aplicação + +#### Serviços de Aplicação + +* `IdentityUserAppService` (implementa `IIdentityUserAppService`): Implementa os casos de uso da interface do usuário de gerenciamento de usuários. +* `IdentityRoleAppService` (implementa `IIdentityRoleAppService`): Implementa os casos de uso da interface do usuário de gerenciamento de funções. +* `IdentityClaimTypeAppService` (implementa `IIdentityClaimTypeAppService`): Implementa os casos de uso da interface do usuário de gerenciamento de tipos de reivindicação. +* `IdentitySettingsAppService` (implementa `IIdentitySettingsAppService`): Usado para obter e atualizar configurações para o módulo de identidade. +* `IdentityUserLookupAppService` (implementa `IIdentityUserLookupAppService`): Usado para obter informações de um usuário por `id` ou `userName`. É destinado a ser usado internamente pelo framework ABP. +* `ProfileAppService` (implementa `IProfileAppService`): Usado para alterar o perfil de um usuário e a senha. +* ```IdentitySecurityLogAppService``` (implementa ```IIdentitySecurityLogAppService```): Implementa os casos de uso da interface do usuário de logs de segurança. +* ```OrganizationUnitAppService``` (implementa ```OrganizationUnitAppService```): Implementa os casos de uso da interface do usuário de gerenciamento de unidades organizacionais. + +### Provedores de Banco de Dados + +Este módulo fornece opções de [Entity Framework Core](../Entity-Framework-Core.md) e [MongoDB](../MongoDB.md) para o banco de dados. + +#### Entity Framework Core + +O pacote NuGet [Volo.Abp.Identity.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.Identity.EntityFrameworkCore) implementa a integração do EF Core. + +##### Tabelas do Banco de Dados + +* **AbpRoles** + * AbpRoleClaims +* **AbpUsers** + * AbpUserClaims + * AbpUserLogins + * AbpUserRoles + * AbpUserTokens +* **AbpClaimTypes** +* **AbpOrganizationUnits** + * AbpOrganizationUnitRoles + * AbpUserOrganizationUnits +* **AbpSecurityLogs** + +#### MongoDB + +O pacote NuGet [Volo.Abp.Identity.MongoDB](https://www.nuget.org/packages/Volo.Abp.Identity.MongoDB) implementa a integração do MongoDB. + +##### Coleções do Banco de Dados + +* **AbpRoles** +* **AbpUsers** +* **AbpClaimTypes** +* **AbpOrganizationUnits** +* **AbpSecurityLogs** + +#### Propriedades Comuns do Banco de Dados + +Você pode definir as seguintes propriedades da classe `AbpIdentityDbProperties` para alterar as opções do banco de dados: + +* `DbTablePrefix` (`Abp` por padrão) é o prefixo para os nomes das tabelas/coleções. +* `DbSchema` (`null` por padrão) é o esquema do banco de dados. +* `ConnectionStringName` (`AbpIdentity` por padrão) é o nome da [string de conexão](../Connection-Strings.md) para este módulo. + +Essas são propriedades estáticas. Se você quiser definir, faça isso no início de sua aplicação (normalmente, em `Program.cs`). \ No newline at end of file diff --git a/docs/pt-BR/Modules/IdentityServer.md b/docs/pt-BR/Modules/IdentityServer.md new file mode 100644 index 0000000000..62e05ceac2 --- /dev/null +++ b/docs/pt-BR/Modules/IdentityServer.md @@ -0,0 +1,175 @@ +# Módulo IdentityServer + +O módulo IdentityServer fornece uma integração completa com o framework [IdentityServer4](https://github.com/IdentityServer/IdentityServer4) (IDS), que oferece recursos avançados de autenticação, como logon único e controle de acesso a API. Este módulo persiste clientes, recursos e outros objetos relacionados ao IDS no banco de dados. **Este módulo foi substituído pelo** [módulo OpenIddict](https://docs.abp.io/en/abp/latest/Modules/OpenIddict) após o ABP v6.0 nos modelos de inicialização. + +> Observação: Você não pode usar os módulos IdentityServer e OpenIddict juntos. Eles são bibliotecas separadas de provedor OpenID para a mesma função. + +## Como instalar + +Você não precisa deste módulo quando estiver usando o módulo OpenIddict. No entanto, se você deseja continuar usando o IdentityServer4 para suas aplicações, você pode instalar este módulo e remover o módulo OpenIddict. Você pode continuar a usá-lo como pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` [CLI](../CLI.md)) para desenvolver seu módulo personalizado. + +### O código-fonte + +O código-fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/identityserver). O código-fonte é licenciado com [MIT](https://choosealicense.com/licenses/mit/), então você pode usá-lo e personalizá-lo livremente. + +## Interface do usuário + +Este módulo implementa a lógica de domínio e as integrações com o banco de dados, mas não fornece nenhuma interface do usuário. A interface de gerenciamento é útil se você precisar adicionar clientes e recursos dinamicamente. Nesse caso, você pode construir a interface de gerenciamento por conta própria ou considerar a compra do [ABP Commercial](https://commercial.abp.io/), que fornece a interface de gerenciamento para este módulo. + +## Relações com outros módulos + +Este módulo é baseado no [módulo Identity](Identity.md) e possui um [pacote de integração](https://www.nuget.org/packages/Volo.Abp.Account.Web.IdentityServer) com o [módulo Account](Account.md). + +## Opções + +### AbpIdentityServerBuilderOptions + +`AbpIdentityServerBuilderOptions` pode ser configurado no método `PreConfigureServices` do seu [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics) do Identity Server. Exemplo: + +````csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure<AbpIdentityServerBuilderOptions>(builder => + { + // Defina as opções aqui... + }); +} +```` + +Propriedades de `AbpIdentityServerBuilderOptions`: + +* `UpdateJwtSecurityTokenHandlerDefaultInboundClaimTypeMap` (padrão: true): Atualiza `JwtSecurityTokenHandler.DefaultInboundClaimTypeMap` para ser compatível com as reivindicações do Identity Server. +* `UpdateAbpClaimTypes` (padrão: true): Atualiza `AbpClaimTypes` para ser compatível com as reivindicações do Identity Server. +* `IntegrateToAspNetIdentity` (padrão: true): Integra ao ASP.NET Identity. +* `AddDeveloperSigningCredential` (padrão: true): Defina como false para suprimir a chamada AddDeveloperSigningCredential() no IIdentityServerBuilder. + +`IIdentityServerBuilder` pode ser configurado no método `PreConfigureServices` do seu [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics) do Identity Server. Exemplo: + +````csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure<IIdentityServerBuilder>(builder => + { + builder.AddSigningCredential(...); + }); +} +```` + +## Internos + +### Camada de Domínio + +#### Agregados + +##### ApiResource + +Os recursos da API são necessários para permitir que os clientes solicitem tokens de acesso. + +* `ApiResource` (raiz do agregado): Representa um recurso da API no sistema. + * `ApiSecret` (coleção): segredos do recurso da API. + * `ApiScope` (coleção): escopos do recurso da API. + * `ApiResourceClaim` (coleção): reivindicações do recurso da API. + +##### Client + +Os clientes representam aplicativos que podem solicitar tokens do seu Identity Server. + +* `Client` (raiz do agregado): Representa um aplicativo cliente do Identity Server. + * `ClientScope` (coleção): Escopos do cliente. + * `ClientSecret` (coleção): Segredos do cliente. + * `ClientGrantType` (coleção): Tipos de concessão do cliente. + * `ClientCorsOrigin` (coleção): Origens CORS do cliente. + * `ClientRedirectUri` (coleção): URIs de redirecionamento do cliente. + * `ClientPostLogoutRedirectUri` (coleção): URIs de redirecionamento de logout do cliente. + * `ClientIdPRestriction` (coleção): Restrições de provedor do cliente. + * `ClientClaim` (coleção): Reivindicações do cliente. + * `ClientProperty` (coleção): Propriedades personalizadas do cliente. + +##### PersistedGrant + +Persisted Grants armazena AuthorizationCodes, RefreshTokens e UserConsent. + +* `PersistedGrant` (raiz do agregado): Representa um PersistedGrant para o servidor de identidade. + +##### IdentityResource + +Os recursos de identidade são dados como ID do usuário, nome ou endereço de e-mail de um usuário. + +* `IdentityResource` (raiz do agregado): Representa um recurso de identidade do Identity Server. + * `IdentityClaim` (coleção): Reivindicações do recurso de identidade. + +#### Repositórios + +Os seguintes repositórios personalizados são definidos para este módulo: + +* `IApiResourceRepository` +* `IClientRepository` +* `IPersistentGrantRepository` +* `IIdentityResourceRepository` + +#### Serviços de Domínio + +Este módulo não contém nenhum serviço de domínio, mas substitui os serviços abaixo; + +* `AbpProfileService` (Usado quando `AbpIdentityServerBuilderOptions.IntegrateToAspNetIdentity` é true) +* `AbpClaimsService` +* `AbpCorsPolicyService` + +### Configurações + +Este módulo não define nenhuma configuração. + +### Camada de Aplicação + +#### Serviços de Aplicação + +* `ApiResourceAppService` (implementa `IApiResourceAppService`): Implementa os casos de uso da interface de gerenciamento de recursos da API. +* `IdentityServerClaimTypeAppService` (implementa `IIdentityServerClaimTypeAppService`): Usado para obter a lista de reivindicações. +* `ApiResourceAppService` (implementa `IApiResourceAppService`): Implementa os casos de uso da interface de gerenciamento de recursos da API. +* `IdentityResourceAppService` (implementa `IIdentityResourceAppService`): Implementa os casos de uso da interface de gerenciamento de recursos de identidade. + +### Provedores de Banco de Dados + +#### Comum + +##### Prefixo de Tabela/Collection e Esquema + +Todas as tabelas/collections usam o prefixo `IdentityServer` por padrão. Defina as propriedades estáticas na classe `AbpIdentityServerDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de Conexão + +Este módulo usa `AbpIdentityServer` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será usada a string de conexão `Default`. + +Consulte a documentação sobre [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter mais detalhes. + +#### Entity Framework Core + +##### Tabelas + +* **IdentityServerApiResources** + * IdentityServerApiSecrets + * IdentityServerApiScopes + * IdentityServerApiScopeClaims + * IdentityServerApiClaims +* **IdentityServerClients** + * IdentityServerClientScopes + * IdentityServerClientSecrets + * IdentityServerClientGrantTypes + * IdentityServerClientCorsOrigins + * IdentityServerClientRedirectUris + * IdentityServerClientPostLogoutRedirectUris + * IdentityServerClientIdPRestrictions + * IdentityServerClientClaims + * IdentityServerClientProperties +* **IdentityServerPersistedGrants** +* **IdentityServerIdentityResources** + * IdentityServerIdentityClaims + +#### MongoDB + +##### Coleções + +* **IdentityServerApiResources** +* **IdentityServerClients** +* **IdentityServerPersistedGrants** +* **IdentityServerIdentityResources** \ No newline at end of file diff --git a/docs/pt-BR/Modules/Index.md b/docs/pt-BR/Modules/Index.md new file mode 100644 index 0000000000..24a7c01169 --- /dev/null +++ b/docs/pt-BR/Modules/Index.md @@ -0,0 +1,32 @@ +# Módulos de Aplicação + +ABP é um **framework de aplicação modular** que consiste em dezenas de **pacotes NuGet & NPM**. Ele também fornece uma infraestrutura completa para construir seus próprios módulos de aplicação, que podem ter entidades, serviços, integração de banco de dados, APIs, componentes de interface do usuário, entre outros. + +Existem **dois tipos de módulos**. Eles não têm nenhuma diferença estrutural, mas são categorizados por funcionalidade e propósito: + +* [**Módulos do framework**](https://github.com/abpframework/abp/tree/dev/framework/src): Estes são **módulos principais do framework** como cache, envio de e-mails, temas, segurança, serialização, validação, integração com o EF Core, integração com o MongoDB... etc. Eles não possuem funcionalidades de aplicação/negócio, mas facilitam o desenvolvimento diário fornecendo infraestrutura comum, integração e abstrações. +* [**Módulos de aplicação**](https://github.com/abpframework/abp/tree/dev/modules): Esses módulos implementam funcionalidades específicas de aplicação/negócio, como blogs, gerenciamento de documentos, gerenciamento de identidade, gerenciamento de locatários... etc. Eles geralmente possuem suas próprias entidades, serviços, APIs e componentes de interface do usuário. + +## Módulos de Aplicação de Código Aberto + +Existem alguns módulos de aplicação **gratuitos e de código aberto** desenvolvidos e mantidos como parte do ABP Framework. + +* [**Conta**](Account.md): Fornece uma interface de usuário para o gerenciamento de contas e permite que o usuário faça login/registo na aplicação. +* [**Registro de Auditoria**](Audit-Logging.md): Persiste registros de auditoria em um banco de dados. +* [**Trabalhos em Segundo Plano**](Background-Jobs.md): Persiste trabalhos em segundo plano ao usar o gerenciador de trabalhos em segundo plano padrão. +* [**Kit CMS**](Cms-Kit/Index.md): Um conjunto de recursos reutilizáveis de *Sistema de Gerenciamento de Conteúdo*. +* [**Documentação**](Docs.md): Usado para criar um site de documentação técnica. A própria documentação do ABP já utiliza este módulo. +* [**Gerenciamento de Recursos**](Feature-Management.md): Usado para persistir e gerenciar os [recursos](../Features.md). +* **[Identidade](Identity.md)**: Gerencia unidades organizacionais, funções, usuários e suas permissões, com base na biblioteca Microsoft Identity. +* [**IdentityServer**](IdentityServer.md): Integra-se ao IdentityServer4. +* [**OpenIddict**](OpenIddict.md): Integra-se ao OpenIddict. +* [**Gerenciamento de Permissões**](Permission-Management.md): Usado para persistir permissões. +* **[Gerenciamento de Configurações](Setting-Management.md)**: Usado para persistir e gerenciar as [configurações](../Settings.md). +* [**Gerenciamento de Locatários**](Tenant-Management.md): Gerencia locatários para uma aplicação [multi-locatário](../Multi-Tenancy.md). +* [**Explorador de Arquivos Virtuais**](Virtual-File-Explorer.md): Fornece uma interface de usuário simples para visualizar arquivos em um [sistema de arquivos virtual](../Virtual-File-System.md). + +Veja [o repositório do GitHub](https://github.com/abpframework/abp/tree/dev/modules) para o código-fonte de todos os módulos. + +## Módulos de Aplicação Comerciais + +A licença [ABP Commercial](https://commercial.abp.io/) fornece **módulos de aplicação pré-construídos adicionais** em cima do framework ABP. Veja a [lista de módulos](https://commercial.abp.io/modules) fornecida pelo ABP Commercial. \ No newline at end of file diff --git a/docs/pt-BR/Modules/OpenIddict.md b/docs/pt-BR/Modules/OpenIddict.md new file mode 100644 index 0000000000..f19b77db5e --- /dev/null +++ b/docs/pt-BR/Modules/OpenIddict.md @@ -0,0 +1,514 @@ +## Módulo ABP OpenIddict + +O módulo OpenIddict fornece uma integração com o [OpenIddict](https://github.com/openiddict/openiddict-core), que oferece recursos avançados de autenticação, como logon único, logoff único e controle de acesso à API. Este módulo persiste aplicativos, escopos e outros objetos relacionados ao OpenIddict no banco de dados. + +## Como instalar + +Este módulo já vem pré-instalado (como pacotes NuGet/NPM). Você pode continuar a usá-lo como um pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` [CLI](../CLI.md)) para desenvolver seu próprio módulo personalizado. + +### O código-fonte + +O código-fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/openiddict). O código-fonte é licenciado pela [MIT](https://choosealicense.com/licenses/mit/), portanto, você pode usá-lo e personalizá-lo livremente. + +## Interface do usuário + +Este módulo implementa a lógica de domínio e as integrações com o banco de dados, mas não fornece nenhuma interface do usuário. A interface de gerenciamento é útil se você precisar adicionar aplicativos e escopos dinamicamente. Nesse caso, você pode construir a interface de gerenciamento por conta própria ou considerar a compra do [ABP Commercial](https://commercial.abp.io/), que fornece a interface de gerenciamento para este módulo. + +## Relações com outros módulos + +Este módulo é baseado no [Módulo de Identidade](Identity.md) e possui um [pacote de integração](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) com o [Módulo de Conta](Account.md). + +## Opções + +### OpenIddictBuilder + +O `OpenIddictBuilder` pode ser configurado no método `PreConfigureServices` do seu [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics) OpenIddict. + +Exemplo: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure<OpenIddictBuilder>(builder => + { + // Defina as opções aqui... + }); +} +``` + +O `OpenIddictBuilder` contém vários métodos de extensão para configurar os serviços do OpenIddict: + +- `AddServer()` registra os serviços do servidor de token OpenIddict no contêiner de DI. Contém as configurações do `OpenIddictServerBuilder`. +- `AddCore()` registra os serviços principais do OpenIddict no contêiner de DI. Contém as configurações do `OpenIddictCoreBuilder`. +- `AddValidation()` registra os serviços de validação de token OpenIddict no contêiner de DI. Contém as configurações do `OpenIddictValidationBuilder`. + +### OpenIddictCoreBuilder + +O `OpenIddictCoreBuilder` contém métodos de extensão para configurar os serviços principais do OpenIddict. + +Exemplo: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure<OpenIddictCoreBuilder>(builder => + { + // Defina as opções aqui... + }); +} +``` + +Esses serviços contêm: + +- Adição de `ApplicationStore`, `AuthorizationStore`, `ScopeStore`, `TokenStore`. +- Substituição de `ApplicationManager`, `AuthorizationManager`, `ScopeManager`, `TokenManager`. +- Substituição de `ApplicationStoreResolver`, `AuthorizationStoreResolver`, `ScopeStoreResolver`, `TokenStoreResolver`. +- Definição de `DefaultApplicationEntity`, `DefaultAuthorizationEntity`, `DefaultScopeEntity`, `DefaultTokenEntity`. + +### OpenIddictServerBuilder + +O `OpenIddictServerBuilder` contém métodos de extensão para configurar os serviços do servidor OpenIddict. + +Exemplo: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure<OpenIddictServerBuilder>(builder => + { + // Defina as opções aqui... + }); +} +``` + +Esses serviços contêm: + +- Registro de claims, escopos. +- Definição do URI `Issuer` que é usado como endereço base para os URIs de endpoint retornados pelo endpoint de descoberta. +- Adição de chaves de assinatura de desenvolvimento, chaves de criptografia/assinatura, credenciais e certificados. +- Adição/remoção de manipuladores de eventos. +- Habilitação/desabilitação de tipos de concessão. +- Definição de URIs de endpoint do servidor de autenticação. + +### OpenIddictValidationBuilder + +O `OpenIddictValidationBuilder` contém métodos de extensão para configurar os serviços de validação do OpenIddict. + +Exemplo: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure<OpenIddictValidationBuilder>(builder => + { + // Defina as opções aqui... + }); +} +``` + +Esses serviços contêm: + +- `AddAudiences()` para servidores de recursos. +- `SetIssuer()` define o URI usado para determinar a localização real do documento de configuração do OAuth 2.0/OpenID Connect ao usar a descoberta do provedor. +- `SetConfiguration()` para configurar `OpenIdConnectConfiguration`. +- `UseIntrospection()` para usar a introspecção em vez da validação local/direta. +- Adição de chave de criptografia, credenciais e certificados. +- Adição/remoção de manipuladores de eventos. +- `SetClientId()` para definir o identificador do cliente `client_id` ao se comunicar com o servidor de autorização remoto (por exemplo, para introspecção). +- `SetClientSecret()` para definir o identificador `client_secret` ao se comunicar com o servidor de autorização remoto (por exemplo, para introspecção). +- `EnableAuthorizationEntryValidation()` para habilitar a validação de autorização para garantir que o `access token` ainda seja válido fazendo uma chamada ao banco de dados para cada solicitação da API. *Observação:* Isso pode ter um impacto negativo no desempenho e só pode ser usado com um servidor de autorização baseado no OpenIddict. +- `EnableTokenEntryValidation()` para habilitar a validação de autorização para garantir que o `access token` ainda seja válido fazendo uma chamada ao banco de dados para cada solicitação da API. *Observação:* Isso pode ter um impacto negativo no desempenho e é necessário quando o servidor OpenIddict está configurado para usar tokens de referência. +- `UseLocalServer()` para registrar os serviços de integração de validação/servidor OpenIddict. +- `UseAspNetCore()` para registrar os serviços de validação do OpenIddict para o ASP.NET Core no contêiner de DI. + +## Internos + +### Camada de Domínio + +#### Agregados + +##### OpenIddictApplication + +OpenIddictApplications representam os aplicativos que podem solicitar tokens do seu servidor OpenIddict. + +- `OpenIddictApplications` (raiz do agregado): Representa um aplicativo OpenIddict. + - `ClientId` (string): O identificador do cliente associado ao aplicativo atual. + - `ClientSecret` (string): O segredo do cliente associado ao aplicativo atual. Pode ser criptografado ou hash para fins de segurança. + - `ConsentType` (string): O tipo de consentimento associado ao aplicativo atual. + - `DisplayName` (string): O nome de exibição associado ao aplicativo atual. + - `DisplayNames` (string): Os nomes de exibição localizados associados ao aplicativo atual serializados como um objeto JSON. + - `Permissions` (string): As permissões associadas ao aplicativo atual, serializadas como um array JSON. + - `PostLogoutRedirectUris` (string): As URLs de retorno de chamada de logoff associadas ao aplicativo atual, serializadas como um array JSON. + - `Properties` (string): As propriedades adicionais associadas ao aplicativo atual serializadas como um objeto JSON ou nulo. + - `RedirectUris` (string): As URLs de retorno de chamada associadas ao aplicativo atual, serializadas como um array JSON. + - `Requirements` (string): Os requisitos associados ao aplicativo atual. + - `Type` (string): O tipo de aplicativo associado ao aplicativo atual. + - `ClientUri` (string): URI para obter mais informações sobre o cliente. + - `LogoUri` (string): URI para o logotipo do cliente. + +##### OpenIddictAuthorization + +OpenIddictAuthorizations são usadas para manter os escopos permitidos e os tipos de fluxo de autorização. + +- `OpenIddictAuthorization` (raiz do agregado): Representa uma autorização OpenIddict. + + - `ApplicationId` (Guid?): O aplicativo associado à autorização atual. + + - `Properties` (string): As propriedades adicionais associadas à autorização atual serializadas como um objeto JSON ou nulo. + + - `Scopes` (string): Os escopos associados à autorização atual, serializados como um array JSON. + + - `Status` (string): O status da autorização atual. + + - `Subject` (string): O assunto associado à autorização atual. + + - `Type` (string): O tipo da autorização atual. + +##### OpenIddictScope + +OpenIddictScopes são usados para manter os escopos dos recursos. + +- `OpenIddictScope` (raiz do agregado): Representa um escopo OpenIddict. + + - `Description` (string): A descrição pública associada ao escopo atual. + + - `Descriptions` (string): As descrições públicas localizadas associadas ao escopo atual, serializadas como um objeto JSON. + + - `DisplayName` (string): O nome de exibição associado ao escopo atual. + + - `DisplayNames` (string): Os nomes de exibição localizados associados ao escopo atual serializados como um objeto JSON. + + - `Name` (string): O nome único associado ao escopo atual. + - `Properties` (string): As propriedades adicionais associadas ao escopo atual serializadas como um objeto JSON ou nulo. + - `Resources` (string): Os recursos associados ao escopo atual, serializados como um array JSON. + +##### OpenIddictToken + +OpenIddictTokens são usados para persistir os tokens do aplicativo. + +- `OpenIddictToken` (raiz do agregado): Representa um token OpenIddict. + + - `ApplicationId` (Guid?): O aplicativo associado ao token atual. + - `AuthorizationId` (Guid?): A autorização associada ao token atual. + - `CreationDate` (DateTime?): A data de criação UTC do token atual. + - `ExpirationDate` (DateTime?): A data de expiração UTC do token atual. + - `Payload` (string): O payload do token atual, se aplicável. Usado apenas para tokens de referência e pode ser criptografado por motivos de segurança. + + - `Properties` (string): As propriedades adicionais associadas ao token atual serializadas como um objeto JSON ou nulo. + - `RedemptionDate` (DateTime?): A data de resgate UTC do token atual. + - `Status` (string): O status da autorização atual. + + - `ReferenceId` (string): O identificador de referência associado ao token atual, se aplicável. Usado apenas para tokens de referência e pode ser criptografado ou hash por motivos de segurança. + + - `Status` (string): O status do token atual. + + - `Subject` (string): O assunto associado ao token atual. + + - `Type` (string): O tipo do token atual. + +#### Armazenamentos + +Este módulo implementa os armazenamentos do OpenIddict: + +- `IAbpOpenIdApplicationStore` +- `IOpenIddictAuthorizationStore` +- `IOpenIddictScopeStore` +- `IOpenIddictTokenStore` + +#### AbpOpenIddictStoreOptions + +Você pode configurar o `PruneIsolationLevel/DeleteIsolationLevel` do `AbpOpenIddictStoreOptions` para definir o nível de isolamento para as operações de armazenamento, pois diferentes bancos de dados têm diferentes níveis de isolamento. + +##### Repositórios + +Os seguintes repositórios personalizados são definidos neste módulo: + +- `IOpenIddictApplicationRepository` +- `IOpenIddictAuthorizationRepository` +- `IOpenIddictScopeRepository` +- `IOpenIddictTokenRepository` + +##### Serviços de Domínio + +Este módulo não contém nenhum serviço de domínio, mas substitui o serviço abaixo: + +- `AbpApplicationManager` usado para popular/obter informações do `AbpApplicationDescriptor` que contém `ClientUri` e `LogoUri`. + +### Provedores de Banco de Dados + +#### Comum + +##### Prefixo de Tabela/Collection e Esquema + +Todas as tabelas/collections usam o prefixo `OpenIddict` por padrão. Defina as propriedades estáticas na classe `AbpOpenIddictDbProperties` se você precisar alterar o prefixo da tabela ou definir um nome de esquema (se suportado pelo seu provedor de banco de dados). + +##### String de Conexão + +Este módulo usa `AbpOpenIddict` como nome da string de conexão. Se você não definir uma string de conexão com esse nome, ela será usada a string de conexão `Default`. + +Consulte a documentação sobre [strings de conexão](https://docs.abp.io/en/abp/latest/Connection-Strings) para obter detalhes. + +#### Entity Framework Core + +##### Tabelas + +- **OpenIddictApplications** +- **OpenIddictAuthorizations** +- **OpenIddictScopes** +- **OpenIddictTokens** + +#### MongoDB + +##### Coleções + +- **OpenIddictApplications** +- **OpenIddictAuthorizations** +- **OpenIddictScopes** +- **OpenIddictTokens** + +## Módulo ASP.NET Core + +Este módulo integra o ASP.NET Core, com controladores MVC embutidos para quatro protocolos. Ele usa o modo de passagem do OpenIddict [Pass-through mode](https://documentation.openiddict.com/guides/index.html#pass-through-mode). + +```cs +AuthorizeController -> connect/authorize +TokenController -> connect/token +LogoutController -> connect/logout +UserInfoController -> connect/userinfo +``` + +> A implementação do **fluxo de dispositivo** será feita no módulo comercial. + +#### AbpOpenIddictAspNetCoreOptions + +`AbpOpenIddictAspNetCoreOptions` pode ser configurado no método `PreConfigureServices` do seu [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics) OpenIddict. + +Exemplo: + +```csharp +PreConfigure<AbpOpenIddictAspNetCoreOptions>(options => +{ + // Defina as opções aqui... +}); +``` + +Propriedades do `AbpOpenIddictAspNetCoreOptions`: + +- `UpdateAbpClaimTypes(default: true)`: Atualiza `AbpClaimTypes` para ser compatível com as reivindicações do Openiddict. +- `AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registra (e gera, se necessário) um certificado de criptografia/assinatura de desenvolvimento específico do usuário. Este é um certificado usado para assinar e criptografar os tokens e apenas para **ambiente de desenvolvimento**. Você deve defini-lo como **false** para ambientes não de desenvolvimento. + +> `AddDevelopmentEncryptionAndSigningCertificate` não pode ser usado em aplicativos implantados no IIS ou no Azure App Service: tentar usá-los no IIS ou no Azure App Service resultará em uma exceção lançada em tempo de execução (a menos que o pool de aplicativos esteja configurado para carregar um perfil de usuário). Para evitar isso, considere criar certificados autoassinados e armazená-los no repositório de certificados X.509 da(s) máquina(s) host. Consulte: https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html#registering-a-development-certificate + +#### Removendo automaticamente Tokens/Autorizações Órfãs + +A tarefa em segundo plano que remove automaticamente tokens/autorizações órfãs pode ser configurada por `TokenCleanupOptions`. + +`TokenCleanupOptions` pode ser configurado no método `ConfigureServices` do seu [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics) OpenIddict. + +Exemplo: + +```csharp +Configure<TokenCleanupOptions>(options => +{ + // Defina as opções aqui... +}); +``` + +Propriedades do `TokenCleanupOptions`: + +- `IsCleanupEnabled` (padrão: true): Habilita/desabilita a limpeza de token. +- `CleanupPeriod` (padrão: 3.600.000 ms): Define o período de limpeza. +- `DisableAuthorizationPruning`: Define um booleano indicando se a poda de autorizações deve ser desabilitada. +- `DisableTokenPruning`: Define um booleano indicando se a poda de tokens deve ser desabilitada. +- `MinimumAuthorizationLifespan` (padrão: 14 dias): Define a vida útil mínima que as autorizações devem ter para serem podadas. Não pode ser inferior a 10 minutos. +- `MinimumTokenLifespan` (padrão: 14 dias): Define a vida útil mínima que os tokens devem ter para serem podados. Não pode ser inferior a 10 minutos. + +#### Atualizando Reivindicações em Access_token e Id_token + +[Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory) pode ser usado para adicionar/remover reivindicações ao `ClaimsPrincipal`. + +O serviço `AbpDefaultOpenIddictClaimsPrincipalHandler` adicionará tipos de reivindicações `Name`, `Email` e `Role` ao `access_token` e `id_token`, outras reivindicações são adicionadas apenas ao `access_token` por padrão e remove a reivindicação secreta `SecurityStampClaimType` do `Identity`. + +Crie um serviço que herde de `IAbpOpenIddictClaimsPrincipalHandler` e adicione-o ao DI para controlar totalmente os destinos das reivindicações. + +```cs +public class MyClaimDestinationsHandler : IAbpOpenIddictClaimsPrincipalHandler, ITransientDependency +{ + public virtual Task HandleAsync(AbpOpenIddictClaimsPrincipalHandlerContext context) + { + foreach (var claim in context.Principal.Claims) + { + if (claim.Type == MyClaims.MyClaimsType) + { + claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken); + } + + if (claim.Type == MyClaims.MyClaimsType2) + { + claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken); + } + } + + return Task.CompletedTask; + } +} + +Configure<AbpOpenIddictClaimsPrincipalOptions>(options => +{ + options.ClaimsPrincipalHandlers.Add<MyClaimDestinationsHandler>(); +}); +``` + +Para obter informações detalhadas, consulte: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html) + +#### Desabilitar a Criptografia do AccessToken + +O ABP desabilita a `criptografia do access token` por padrão para compatibilidade, mas pode ser habilitada manualmente, se necessário. + +```cs +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure<OpenIddictServerBuilder>(builder => + { + builder.Configure(options => options.DisableAccessTokenEncryption = false); + }); +} +``` + +https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption + +### Processo de Solicitação/Resposta + +O `OpenIddict.Server.AspNetCore` adiciona um esquema de autenticação (`Name: OpenIddict.Server.AspNetCore, handler: OpenIddictServerAspNetCoreHandler`) e implementa a interface `IAuthenticationRequestHandler`. + +Ele será executado primeiro no `AuthenticationMiddleware` e pode interromper o processamento da solicitação atual. Caso contrário, o `DefaultAuthenticateScheme` será chamado e continuará a executar o pipeline. + +O `OpenIddictServerAspNetCoreHandler` chamará vários manipuladores embutidos (manipulando solicitações e respostas) e o manipulador processará de acordo com o contexto ou ignorará a lógica que não tem relação com ele. + +Exemplo de uma solicitação de token: + +``` +POST /connect/token HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + + grant_type=password& + client_id=AbpApp& + client_secret=1q2w3e*& + username=admin& + password=1q2w3E*& + scope=AbpAPI offline_access +``` + +Esta solicitação será processada por vários manipuladores. Eles confirmarão o tipo de endpoint da solicitação, verificarão `HTTP/HTTPS`, verificarão se os parâmetros da solicitação (`client, scope`, etc.) são válidos e existem no banco de dados, etc. Várias verificações de protocolo. E construir um objeto `OpenIddictRequest`, Se houver erros, o conteúdo da resposta pode ser definido e interromper diretamente a solicitação atual. + +Se tudo estiver ok, a solicitação irá para nosso controlador de processamento (por exemplo, `TokenController`), podemos obter um `OpenIddictRequest` da solicitação HTTP neste momento. O restante será baseado neste objeto. + +Verifique o `username` e `password` na solicitação. Se estiver correto, crie um objeto `ClaimsPrincipal` e retorne um `SignInResult`, que usa o nome do esquema de autenticação `OpenIddict.Validation.AspNetCore`, chamará o `OpenIddictServerAspNetCoreHandler` para processamento. + +O `OpenIddictServerAspNetCoreHandler` fará algumas verificações para gerar json e substituir o conteúdo da resposta HTTP. + +O `ForbidResult` `ChallengeResult` são todos os tipos de processamento acima. + +Se você precisar personalizar o OpenIddict, será necessário substituir/excluir/adicionar novos manipuladores e fazer com que ele seja executado na ordem correta. + +Consulte: https://documentation.openiddict.com/guides/index.html#events-model + +### PKCE + +https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html + +### Definindo o Tempo de Vida dos Tokens + +Atualize o método `PreConfigureServices` do arquivo AuthServerModule (ou HttpApiHostModule se você não tiver um servidor de autenticação separado) : + +```csharp +PreConfigure<OpenIddictServerBuilder>(builder => +{ + builder.SetAuthorizationCodeLifetime(TimeSpan.FromMinutes(30)); + builder.SetAccessTokenLifetime(TimeSpan.FromMinutes(30)); + builder.SetIdentityTokenLifetime(TimeSpan.FromMinutes(30)); + builder.SetRefreshTokenLifetime(TimeSpan.FromDays(14)); +}); +``` + +### Token de Atualização + +Para usar o token de atualização, ele deve ser suportado pelo OpenIddictServer e o `refresh_token` deve ser solicitado pelo aplicativo. + +> **Observação:** O aplicativo Angular já está configurado para usar o `refresh_token`. + +#### Configurando o OpenIddictServer + +Atualize o **OpenIddictDataSeedContributor**, adicione `OpenIddictConstants.GrantTypes.RefreshToken` aos tipos de concessão no método `CreateApplicationAsync`: + +```csharp +await CreateApplicationAsync( + ... + grantTypes: new List<string> //Fluxo híbrido + { + OpenIddictConstants.GrantTypes.AuthorizationCode, + OpenIddictConstants.GrantTypes.Implicit, + OpenIddictConstants.GrantTypes.RefreshToken, + }, + ... +``` + +> **Observação:** Você precisa recriar esse cliente se já tiver gerado o banco de dados. + +#### Configurando o Aplicativo: + +Você precisa solicitar o escopo **offline_access** para poder receber o `refresh_token`. + +Nos aplicativos **Razor/MVC, Blazor-Server**, adicione `options.Scope.Add("offline_access");` às opções **OpenIdConnect**. Esses modelos de aplicativo usam autenticação por cookie por padrão e têm as opções de expiração do cookie definidas como: + +```csharp +.AddCookie("Cookies", options => +{ + options.ExpireTimeSpan = TimeSpan.FromDays(365); +}) +``` + +[Cookie ExpireTimeSpan ignorará a expiração do access_token](https://learn.microsoft.com/en-us/dotnet/api/Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions.ExpireTimeSpan?view=aspnetcore-7.0&viewFallbackFrom=net-7.0) e o access_token expirado ainda será válido se for definido com um valor maior que o `refresh_token lifetime`. É recomendável manter o **Cookie ExpireTimeSpan** e o **Refresh Token lifetime** iguais, para que o novo token seja persistido no cookie. + +Nos aplicativos **Blazor wasm**, adicione `options.ProviderOptions.DefaultScopes.Add("offline_access");` às opções **AddOidcAuthentication**. + +Nos aplicativos **Angular**, adicione `offline_access` aos escopos **oAuthConfig** no arquivo *environment.ts*. (Os aplicativos Angular já têm essa configuração). + +## Sobre a localização + +Não localizamos nenhuma mensagem de erro no módulo OpenIddict, porque a especificação OAuth 2.0 restringe o conjunto de caracteres que você pode usar para os parâmetros de erro e error_description: + +> A.7. "error" Syntax +> O elemento "error" é definido nas Seções 4.1.2.1, 4.2.2.1, 5.2, 7.2 e 8.5: + +``` +error = 1*NQSCHAR +``` + +> A.8. "error_description" Syntax +> O elemento "error_description" é definido nas Seções 4.1.2.1, 4.2.2.1, 5.2 e 7.2: + +``` +error-description = 1*NQSCHAR +NQSCHAR = %x20-21 / %x23-5B / %x5D-7E +``` + +## Projetos de demonstração + +No diretório `app` do módulo, existem seis projetos (incluindo `angular`) + +* `OpenIddict.Demo.Server`: Um aplicativo abp com módulos integrados (possui dois `clientes` e um `escopo`). +* `OpenIddict.Demo.API`: Aplicativo ASP NET Core API usando autenticação JwtBearer. +* `OpenIddict.Demo.Client.Mvc`: Aplicativo ASP NET Core MVC usando `OpenIdConnect` para autenticação. +* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` para testar os vários endpoints do OpenIddict e chamar a API do `OpenIddict.Demo.API`. +* `OpenIddict.Demo.Client.BlazorWASM:` Aplicativo Blazor ASP NET Core usando `OidcAuthentication` para autenticação. +* `angular`: Um aplicativo angular que integra os módulos abp ng e usa oauth para autenticação. + +#### Como executar? + +Confirme a string de conexão do `appsettings.json` no projeto `OpenIddict.Demo.Server`. A execução do projeto criará automaticamente o banco de dados e inicializará os dados. +Após executar o projeto `OpenIddict.Demo.API`, você pode executar o restante dos projetos para testar. + +## Guia de Migração + +[Guia de Migração Passo a Passo do IdentityServer para o OpenIddict](../Migration-Guides/OpenIddict-Step-by-Step.md) \ No newline at end of file diff --git a/docs/pt-BR/Modules/Permission-Management.md b/docs/pt-BR/Modules/Permission-Management.md new file mode 100644 index 0000000000..ff3071c4db --- /dev/null +++ b/docs/pt-BR/Modules/Permission-Management.md @@ -0,0 +1,109 @@ +# Módulo de Gerenciamento de Permissões + +Este módulo implementa o `IPermissionStore` para armazenar e gerenciar valores de permissões em um banco de dados. + +> Este documento aborda apenas o módulo de gerenciamento de permissões que persiste os valores de permissão em um banco de dados. Consulte o documento de [Autorização](../Authorization.md) para entender os sistemas de autorização e permissão. + +## Como Instalar + +Este módulo já vem pré-instalado (como pacotes NuGet/NPM). Você pode continuar a usá-lo como pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` da [CLI](../CLI.md)) para desenvolver seu próprio módulo personalizado. + +### O Código-fonte + +O código-fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/permission-management). O código-fonte é licenciado com a licença [MIT](https://choosealicense.com/licenses/mit/), portanto, você pode usá-lo e personalizá-lo livremente. + +## Interface do Usuário + +### Diálogo de Gerenciamento de Permissões + +O módulo de gerenciamento de permissões fornece um diálogo reutilizável para gerenciar permissões relacionadas a um objeto. Por exemplo, o [Módulo de Identidade](Identity.md) o utiliza para gerenciar as permissões de usuários e funções. A imagem a seguir mostra a página de Gerenciamento de Funções do Módulo de Identidade: + +![permissions-module-open-dialog](../images/permissions-module-open-dialog.png) + +Quando você clica em *Ações* -> *Permissões* para uma função, o diálogo de gerenciamento de permissões é aberto. Uma captura de tela de exemplo deste diálogo: + +![permissions-module-dialog](../images/permissions-module-dialog.png) + +Neste diálogo, você pode conceder permissões para a função selecionada. As abas no lado esquerdo representam os principais grupos de permissões e o lado direito contém as permissões definidas no grupo selecionado. + +## IPermissionManager + +`IPermissionManager` é o serviço principal fornecido por este módulo. Ele é usado para ler e alterar os valores de permissão. `IPermissionManager` é normalmente usado pelo *Diálogo de Gerenciamento de Permissões*. No entanto, você pode injetá-lo se precisar definir um valor de permissão. + +> Se você apenas deseja ler/verificar os valores de permissão para o usuário atual, use o `IAuthorizationService` ou o atributo `[Authorize]`, conforme explicado no documento de [Autorização](../Authorization.md). + +**Exemplo: Conceder permissões para funções e usuários usando o serviço `IPermissionManager`** + +````csharp +public class MeuServico : ITransientDependency +{ + private readonly IPermissionManager _permissionManager; + + public MeuServico(IPermissionManager permissionManager) + { + _permissionManager = permissionManager; + } + + public async Task ConcederPermissaoParaFuncaoDemoAsync( + string nomeFuncao, string permissao) + { + await _permissionManager + .SetForRoleAsync(nomeFuncao, permissao, true); + } + + public async Task ConcederPermissaoParaUsuarioDemoAsync( + Guid idUsuario, string nomeFuncao, string permissao) + { + await _permissionManager + .SetForUserAsync(idUsuario, permissao, true); + } +} +```` + +## Provedores de Gerenciamento de Permissões + +O Módulo de Gerenciamento de Permissões é extensível, assim como o [sistema de permissões](../Authorization.md). Você pode estendê-lo definindo provedores de gerenciamento de permissões. + +O [Módulo de Identidade](Identity.md) define os seguintes provedores de gerenciamento de permissões: + +* `UserPermissionManagementProvider`: Gerencia permissões baseadas em usuários. +* `RolePermissionManagementProvider`: Gerencia permissões baseadas em funções. + +`IPermissionManager` usa esses provedores quando você obtém/define permissões. Você pode definir seu próprio provedor implementando o `IPermissionManagementProvider` ou herdando da classe base `PermissionManagementProvider`. + +**Exemplo:** + +````csharp +public class CustomPermissionManagementProvider : PermissionManagementProvider +{ + public override string Name => "Custom"; + + public CustomPermissionManagementProvider( + IPermissionGrantRepository permissionGrantRepository, + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant) + : base( + permissionGrantRepository, + guidGenerator, + currentTenant) + { + } +} +```` + +A classe base `PermissionManagementProvider` faz a implementação padrão (usando o `IPermissionGrantRepository`) para você. Você pode substituir os métodos base conforme necessário. Cada provedor deve ter um nome exclusivo, que é `Custom` neste exemplo (mantenha-o curto, pois ele é salvo no banco de dados para cada registro de valor de permissão). + +Depois de criar sua classe de provedor, você deve registrá-la usando a classe de opções `PermissionManagementOptions` [options class](../Options.md): + +````csharp +Configure<PermissionManagementOptions>(options => +{ + options.ManagementProviders.Add<CustomPermissionManagementProvider>(); +}); +```` + +A ordem dos provedores é importante. Os provedores são executados na ordem inversa. Isso significa que o `CustomPermissionManagementProvider` é executado primeiro neste exemplo. Você pode inserir seu provedor em qualquer ordem na lista `Providers`. + +## Veja também + +* [Autorização](../Authorization.md) \ No newline at end of file diff --git a/docs/pt-BR/Modules/Setting-Management.md b/docs/pt-BR/Modules/Setting-Management.md new file mode 100644 index 0000000000..5cfde745b9 --- /dev/null +++ b/docs/pt-BR/Modules/Setting-Management.md @@ -0,0 +1,313 @@ +# Módulo de Gerenciamento de Configurações + +O Módulo de Gerenciamento de Configurações implementa a interface `ISettingStore` (consulte [o sistema de configurações](../Settings.md)) para armazenar os valores das configurações em um banco de dados e fornece a interface `ISettingManager` para gerenciar (alterar) os valores das configurações no banco de dados. + +> O módulo de Gerenciamento de Configurações já está instalado e configurado nos [modelos de inicialização](../Startup-Templates/Index.md). Portanto, na maioria das vezes, você não precisa adicionar manualmente este módulo à sua aplicação. + +## ISettingManager + +`ISettingManager` é usado para obter e definir os valores das configurações. Exemplos: + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.SettingManagement; + +namespace Demo +{ + public class MyService : ITransientDependency + { + private readonly ISettingManager _settingManager; + + // Injeta o serviço ISettingManager + public MyService(ISettingManager settingManager) + { + _settingManager = settingManager; + } + + public async Task FooAsync() + { + Guid user1Id = ...; + Guid tenant1Id = ...; + + // Obtém/define um valor de configuração para o usuário atual ou para o usuário especificado + + string layoutType1 = + await _settingManager.GetOrNullForCurrentUserAsync("App.UI.LayoutType"); + string layoutType2 = + await _settingManager.GetOrNullForUserAsync("App.UI.LayoutType", user1Id); + + await _settingManager.SetForCurrentUserAsync("App.UI.LayoutType", "LeftMenu"); + await _settingManager.SetForUserAsync(user1Id, "App.UI.LayoutType", "LeftMenu"); + + // Obtém/define um valor de configuração para o locatário atual ou para o locatário especificado + + string layoutType3 = + await _settingManager.GetOrNullForCurrentTenantAsync("App.UI.LayoutType"); + string layoutType4 = + await _settingManager.GetOrNullForTenantAsync("App.UI.LayoutType", tenant1Id); + + await _settingManager.SetForCurrentTenantAsync("App.UI.LayoutType", "LeftMenu"); + await _settingManager.SetForTenantAsync(tenant1Id, "App.UI.LayoutType", "LeftMenu"); + + // Obtém/define um valor de configuração global e padrão + + string layoutType5 = + await _settingManager.GetOrNullGlobalAsync("App.UI.LayoutType"); + string layoutType6 = + await _settingManager.GetOrNullDefaultAsync("App.UI.LayoutType"); + + await _settingManager.SetGlobalAsync("App.UI.LayoutType", "TopMenu"); + } + } +} + +```` + +Portanto, você pode obter ou definir um valor de configuração para diferentes provedores de valores de configuração (Padrão, Global, Usuário, Locatário... etc). + +> Use a interface `ISettingProvider` em vez da `ISettingManager` se você apenas precisa ler os valores das configurações, pois ela implementa o cache e suporta todos os cenários de implantação. Você pode usar a `ISettingManager` se estiver criando uma interface de gerenciamento de configurações. + +### Cache de Configurações + +Os valores das configurações são armazenados em cache usando o sistema de [cache distribuído](../Caching.md). Sempre use o `ISettingManager` para alterar os valores das configurações, pois ele gerencia o cache para você. + +## Provedores de Gerenciamento de Configurações + +O módulo de Gerenciamento de Configurações é extensível, assim como o [sistema de configurações](../Settings.md). Você pode estendê-lo definindo provedores de gerenciamento de configurações. Existem 5 provedores de gerenciamento de configurações pré-construídos registrados na seguinte ordem: + +* `DefaultValueSettingManagementProvider`: Obtém o valor do valor padrão da definição da configuração. Ele não pode definir o valor padrão, pois os valores padrão são codificados na definição da configuração. +* `ConfigurationSettingManagementProvider`: Obtém o valor do serviço [IConfiguration](../Configuration.md). Ele não pode definir o valor de configuração, pois não é possível alterar os valores de configuração em tempo de execução. +* `GlobalSettingManagementProvider`: Obtém ou define o valor global (em todo o sistema) para uma configuração. +* `TenantSettingManagementProvider`: Obtém ou define o valor da configuração para um locatário. +* `UserSettingManagementProvider`: Obtém o valor da configuração para um usuário. + +O `ISettingManager` usa os provedores de gerenciamento de configurações nos métodos de obtenção/definição. Normalmente, cada provedor de gerenciamento de configurações define métodos de extensão no serviço `ISettingManagement` (como `SetForUserAsync` definido pelo provedor de gerenciamento de configurações de usuário). + +Se você deseja criar seu próprio provedor, implemente a interface `ISettingManagementProvider` ou herde da classe base `SettingManagementProvider`: + +````csharp +public class CustomSettingProvider : SettingManagementProvider, ITransientDependency +{ + public override string Name => "Custom"; + + public CustomSettingProvider(ISettingManagementStore store) + : base(store) + { + } +} +```` + +A classe base `SettingManagementProvider` faz a implementação padrão (usando o `ISettingManagementStore`) para você. Você pode substituir os métodos base conforme necessário. Todo provedor deve ter um nome exclusivo, que é `Custom` neste exemplo (mantenha-o curto, pois ele é salvo no banco de dados para cada registro de valor de configuração). + +Depois de criar sua classe de provedor, você deve registrá-la usando a classe de opções `SettingManagementOptions` [options class](../Options.md): + +````csharp +Configure<SettingManagementOptions>(options => +{ + options.Providers.Add<CustomSettingProvider>(); +}); +```` + +A ordem dos provedores é importante. Os provedores são executados na ordem inversa. Isso significa que o `CustomSettingProvider` é executado primeiro neste exemplo. Você pode inserir seu provedor em qualquer ordem na lista `Providers`. + +## Veja também + +* [Configurações](../Settings.md) + +## Interface de Gerenciamento de Configurações + +O módulo de Gerenciamento de Configurações fornece a interface de configuração de e-mail por padrão. + +![Interface de Configuração de E-mail](../images/setting-management-email-ui.png) + +> Você pode clicar no botão Enviar e-mail de teste para enviar um e-mail de teste e verificar suas configurações de e-mail. + +Ele é extensível; você pode adicionar suas guias a esta página para as configurações de sua aplicação. + +### Interface de Usuário MVC + +#### Criar um Componente de Visualização de Configuração + +Crie a pasta `MySettingGroup` dentro da pasta `Components`. Adicione um novo componente de visualização. Nomeie-o como `MySettingGroupViewComponent`: + +![MySettingGroupViewComponent](../images/my-setting-group-view-component.png) + +Abra o arquivo `MySettingGroupViewComponent.cs` e altere todo o conteúdo conforme mostrado abaixo: + +```csharp +public class MySettingGroupViewComponent : AbpViewComponent +{ + public virtual IViewComponentResult Invoke() + { + return View("~/Components/MySettingGroup/Default.cshtml"); + } +} +``` + +> Você também pode usar o método `InvokeAsync`, neste exemplo, usamos o método `Invoke`. + +#### Default.cshtml + +Crie um arquivo `Default.cshtml` dentro da pasta `MySettingGroup`. + +Abra o arquivo `Default.cshtml` e altere todo o conteúdo conforme mostrado abaixo: + +```html +<div> + <p>Página do meu grupo de configurações</p> +</div> +``` + +#### BookStoreSettingPageContributor + +Crie um arquivo `BookStoreSettingPageContributor.cs` dentro da pasta `Settings`: + +![BookStoreSettingPageContributor](../images/my-setting-group-page-contributor.png) + +O conteúdo do arquivo é mostrado abaixo: + +```csharp +public class BookStoreSettingPageContributor : ISettingPageContributor +{ + public Task ConfigureAsync(SettingPageCreationContext context) + { + context.Groups.Add( + new SettingPageGroup( + "Volo.Abp.MySettingGroup", + "MySettingGroup", + typeof(MySettingGroupViewComponent), + order : 1 + ) + ); + + return Task.CompletedTask; + } + + public Task<bool> CheckPermissionsAsync(SettingPageCreationContext context) + { + // Você pode verificar as permissões aqui + return Task.FromResult(true); + } +} +``` + +Abra o arquivo `BookStoreWebModule.cs` e adicione o seguinte código: + +```csharp +Configure<SettingManagementPageOptions>(options => +{ + options.Contributors.Add(new BookStoreSettingPageContributor()); +}); +``` + +#### Executar a Aplicação + +Acesse a rota `/SettingManagement` para ver as alterações: + +![Guia de Configurações Personalizadas](../images/my-setting-group-ui.png) + +### Interface de Usuário Blazor + +#### Criar um Componente Razor + +Crie a pasta `MySettingGroup` dentro da pasta `Pages`. Adicione um novo componente Razor. Nomeie-o como `MySettingGroupComponent`: + +![MySettingGroupComponent](../images/my-setting-group-component.png) + +Abra o arquivo `MySettingGroupComponent.razor` e altere todo o conteúdo conforme mostrado abaixo: + +```csharp +<Row> + <p>meu grupo de configurações</p> +</Row> +``` + +#### BookStoreSettingComponentContributor + +Crie um arquivo `BookStoreSettingComponentContributor.cs` dentro da pasta `Settings`: + +![BookStoreSettingComponentContributor](../images/my-setting-group-component-contributor.png) + +O conteúdo do arquivo é mostrado abaixo: + +```csharp +public class BookStoreSettingComponentContributor : ISettingComponentContributor +{ + public Task ConfigureAsync(SettingComponentCreationContext context) + { + context.Groups.Add( + new SettingComponentGroup( + "Volo.Abp.MySettingGroup", + "MySettingGroup", + typeof(MySettingGroupComponent), + order : 1 + ) + ); + + return Task.CompletedTask; + } + + public Task<bool> CheckPermissionsAsync(SettingComponentCreationContext context) + { + // Você pode verificar as permissões aqui + return Task.FromResult(true); + } +} +``` + +Abra o arquivo `BookStoreBlazorModule.cs` e adicione o seguinte código: + +```csharp +Configure<SettingManagementComponentOptions>(options => +{ + options.Contributors.Add(new BookStoreSettingComponentContributor()); +}); +``` + +#### Executar a Aplicação + +Acesse a rota `/setting-management` para ver as alterações: + +![Guia de Configurações Personalizadas](../images/my-setting-group-blazor.png) + +### Interface de Usuário Angular + +#### Criar um Componente + +Crie um componente com o seguinte comando: + +```bash +yarn ng generate component my-settings +``` + +Abra o arquivo `app.component.ts` e modifique o arquivo conforme mostrado abaixo: + +```js +import { Component } from '@angular/core'; +import { SettingTabsService } from '@abp/ng.setting-management/config'; // importando SettingTabsService +import { MySettingsComponent } from './my-settings/my-settings.component'; // importando MySettingsComponent + +@Component(/* metadados do componente */) +export class AppComponent { + constructor(private settingTabs: SettingTabsService) // injetando MySettingsComponent + { + // adicionado abaixo + settingTabs.add([ + { + name: 'MySettings', + order: 1, + requiredPolicy: 'chave da política aqui', + component: MySettingsComponent, + }, + ]); + } +} +``` + +#### Executar a Aplicação + +Acesse a rota `/setting-management` para ver as alterações: + +![Guia de Configurações Personalizadas](../images/custom-settings.png) \ No newline at end of file diff --git a/docs/pt-BR/Modules/Tenant-Management.md b/docs/pt-BR/Modules/Tenant-Management.md new file mode 100644 index 0000000000..2ff8d3c0a2 --- /dev/null +++ b/docs/pt-BR/Modules/Tenant-Management.md @@ -0,0 +1,134 @@ +# Módulo de Gerenciamento de Inquilinos + +O [Multi-Tenancy](../Multi-Tenancy.md) é uma das principais características do ABP Framework. Ele fornece a infraestrutura fundamental para construir sua própria solução SaaS (Software-as-a-Service). O sistema de multi-tenancy do ABP abstrai onde seus inquilinos são armazenados, fornecendo a interface `ITenantStore`. Tudo que você precisa fazer é implementar essa interface. + +**O módulo de gerenciamento de inquilinos é uma implementação da interface `ITenantStore`. Ele armazena inquilinos em um banco de dados. Ele também fornece uma interface de usuário para gerenciar seus inquilinos e suas [funcionalidades](../Features.md).** + +> Por favor, **consulte a documentação do [Multi-Tenancy](../Multi-Tenancy.md)** para entender o sistema de multi-tenancy do ABP Framework. Este documento se concentra no módulo de gerenciamento de inquilinos. + +### Sobre o Módulo SaaS Comercial do ABP + +O [Módulo SaaS](https://commercial.abp.io/modules/Volo.Saas) é uma implementação alternativa deste módulo com mais funcionalidades e possibilidades. Ele é distribuído como parte da assinatura do [ABP Commercial](https://commercial.abp.io/). + +## Como Instalar + +Este módulo vem pré-instalado (como pacotes NuGet/NPM) quando você [cria uma nova solução](https://abp.io/get-started) com o ABP Framework. Você pode continuar a usá-lo como pacote e obter atualizações facilmente, ou pode incluir seu código-fonte em sua solução (consulte o comando `get-source` da [CLI](../CLI.md)) para desenvolver seu próprio módulo personalizado. + +### O Código Fonte + +O código-fonte deste módulo pode ser acessado [aqui](https://github.com/abpframework/abp/tree/dev/modules/tenant-management). O código-fonte é licenciado com [MIT](https://choosealicense.com/licenses/mit/), então você pode usá-lo e personalizá-lo livremente. + +## Interface de Usuário + +Este módulo adiciona o item de menu "*Administração -> Gerenciamento de Inquilinos -> Inquilinos*" ao menu principal do aplicativo, que abre a página mostrada abaixo: + +![module-tenant-management-page](../images/module-tenant-management-page.png) + +Nesta página, você vê todos os inquilinos. Você pode criar um novo inquilino conforme mostrado abaixo: + +![module-tenant-management-new-tenant](../images/module-tenant-management-new-tenant.png) + +Neste modal; + +* **Nome**: O nome único do inquilino. Se você usar subdomínios para seus inquilinos (como https://algum-inquilino.seu-domínio.com), este será o nome do subdomínio. +* **Endereço de E-mail do Administrador**: Endereço de e-mail do usuário administrador para este inquilino. +* **Senha do Administrador**: A senha do usuário administrador para este inquilino. + +Quando você clica no botão *Ações* próximo a um inquilino, você verá as ações que pode realizar: + +![module-tenant-management-actions](../images/module-tenant-management-actions.png) + +### Gerenciando as Funcionalidades do Inquilino + +A ação Funcionalidades abre um modal para habilitar/desabilitar/configurar [funcionalidades](../Features.md) para o inquilino relacionado. Aqui, um exemplo de modal: + +![features-modal](../images/features-modal.png) + +### Gerenciando as Funcionalidades do Host + +O botão *Gerenciar Funcionalidades do Host* é usado para configurar as funcionalidades do lado do host, se você usar as funcionalidades do seu aplicativo também no lado do host. + +## Eventos Distribuídos + +Este módulo define os seguintes ETOs (Event Transfer Objects) para permitir que você se inscreva em alterações nas entidades do módulo; + +- `TenantEto` é publicado em alterações feitas em uma entidade `Tenant`. + +**Exemplo: Receber uma notificação quando um novo inquilino for criado** + +```cs +public class MeuManipulador : + IDistributedEventHandler<EntityCreatedEto<TenantEto>>, + ITransientDependency +{ + public async Task HandleEventAsync(EntityCreatedEto<TenantEto> eventData) + { + TenantEto tenant = eventData.Entity; + // TODO: ... + } +} +``` + + + +`TenantEto` é configurado para publicar automaticamente os eventos. Você deve configurar-se para os outros. Consulte o documento [Distributed Event Bus](https://github.com/abpframework/abp/blob/rel-7.3/docs/en/Distributed-Event-Bus.md) para aprender detalhes dos eventos pré-definidos. + +> A inscrição nos eventos distribuídos é especialmente útil para cenários distribuídos (como arquitetura de microsserviços). Se você estiver construindo um aplicativo monolítico ou ouvindo eventos no mesmo processo que executa o Módulo de Gerenciamento de Inquilinos, então a inscrição nos [eventos locais](https://github.com/abpframework/abp/blob/rel-7.3/docs/en/Local-Event-Bus.md) pode ser mais eficiente e fácil. + +## Internos + +Esta seção pode ser usada como referência se você quiser [personalizar](../Customizing-Application-Modules-Guide.md) este módulo sem alterar [seu código-fonte](https://github.com/abpframework/abp/tree/dev/modules/tenant-management). + +### Camada de Domínio + +#### Agregados + +* `Tenant` + +#### Repositórios + +* `ITenantRepository` + +#### Serviços de Domínio + +* `TenantManager` + +### Camada de Aplicação + +#### Serviços de Aplicação + +* `TenantAppService` + +#### Permissões + +- `AbpTenantManagement.Tenants`: Gerenciamento de inquilinos. +- `AbpTenantManagement.Tenants.Create`: Criar um novo inquilino. +- `AbpTenantManagement.Tenants.Update`: Editar um inquilino existente. +- `AbpTenantManagement.Tenants.Delete`: Excluir um inquilino existente. +- `AbpTenantManagement.Tenants.ManageFeatures`: Gerenciar as funcionalidades dos inquilinos. + +### Integração com o Entity Framework Core + +* `TenantManagementDbContext` (implementa `ITenantManagementDbContext`) + +**Tabelas do Banco de Dados:** + +* `AbpTenants` +* `AbpTenantConnectionStrings` + +### Integração com o MongoDB + +* `TenantManagementMongoDbContext` (implementa `ITenantManagementMongoDbContext`) + +**Coleções do Banco de Dados:** + +* `AbpTenants` (inclui também a string de conexão) + +## Avisos + +O ABP Framework permite usar a abordagem *banco de dados por inquilino*, que permite que um inquilino tenha um banco de dados dedicado. Este módulo possui a infraestrutura fundamental para tornar essa implementação possível (consulte seu código-fonte), no entanto, ele não implementa a camada de aplicação e as funcionalidades de interface do usuário para fornecê-lo como uma implementação pronta para uso. Você pode implementar esses recursos por conta própria ou considerar o uso do [Módulo SaaS Comercial do ABP](https://docs.abp.io/en/commercial/latest/modules/saas), que o implementa completamente e fornece muito mais recursos de negócios. + +## Veja Também + +* [Multi-Tenancy](../Multi-Tenancy.md) +* [Módulo SaaS Comercial do ABP](https://docs.abp.io/en/commercial/latest/modules/saas) \ No newline at end of file diff --git a/docs/pt-BR/Modules/Virtual-File-Explorer.md b/docs/pt-BR/Modules/Virtual-File-Explorer.md new file mode 100644 index 0000000000..42cbd9f044 --- /dev/null +++ b/docs/pt-BR/Modules/Virtual-File-Explorer.md @@ -0,0 +1,87 @@ +# Módulo de Explorador de Arquivos Virtual + +## O que é o Módulo de Explorador de Arquivos Virtual? + +O Módulo de Explorador de Arquivos Virtual fornece uma interface de usuário simples para visualizar todos os arquivos no [sistema de arquivos virtual](../Virtual-File-System.md). + +> O Módulo de Explorador de Arquivos Virtual não está instalado nos [modelos de inicialização](../Startup-Templates/Index.md). Portanto, você precisa adicionar manualmente este módulo à sua aplicação. + +### Instalação + +#### 1- Usando o ABP CLI + +Recomenda-se usar o [ABP CLI](../CLI.md) para instalar o módulo. Abra a janela do CMD no diretório do arquivo de solução (`.sln`) e execute o seguinte comando: + +``` +abp add-module Volo.VirtualFileExplorer +``` + +> Se você ainda não o fez, primeiro precisa instalar o [ABP CLI](../CLI.md). Para outras opções de instalação, consulte [a página de descrição do pacote](https://abp.io/package-detail/Volo.Abp.VirtualFileExplorer.Web). + +#### 2- Instalação manual + +Ou você também pode instalar manualmente o pacote nuget no projeto `Acme.MyProject.Web`: + +* Instale o pacote nuget [Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) no projeto `Acme.MyProject.Web`. + + `Install-Package Volo.Abp.VirtualFileExplorer.Web` + +##### 2.1- Adicionando Dependências do Módulo + + * Abra `MyProjectWebModule.cs` e adicione `typeof(AbpVirtualFileExplorerWebModule)` como mostrado abaixo; + + ```csharp + [DependsOn( + typeof(AbpVirtualFileExplorerWebModule), + typeof(MyProjectApplicationModule), + typeof(MyProjectEntityFrameworkCoreModule), + typeof(AbpAutofacModule), + typeof(AbpIdentityWebModule), + typeof(AbpAccountWebModule), + typeof(AbpAspNetCoreMvcUiBasicThemeModule) + )] + public class MyProjectWebModule : AbpModule + { + //... + } + ``` + +##### 2.2- Adicionando Pacote NPM + + * Abra `package.json` e adicione `@abp/virtual-file-explorer": "^2.9.0` como mostrado abaixo: + + ```json + { + "version": "1.0.0", + "name": "my-app", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.9.0", + "@abp/virtual-file-explorer": "^2.9.0" + } + } + ``` + + Em seguida, abra o terminal de linha de comando na pasta do projeto `Acme.MyProject.Web` e execute o seguinte comando: + +````bash +abp install-libs +```` + +Isso é tudo, agora execute a aplicação e navegue até `/VirtualFileExplorer`. Você verá a página do explorador de arquivos virtual: + +![Virtual-File-Explorer](../images/virtual-file-explorer.png) + +### Opções + +Você pode desativar o módulo de explorador de arquivos virtual através das opções `AbpVirtualFileExplorerOptions`: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure<AbpVirtualFileExplorerOptions>(options => + { + options.IsEnabled = false; + }); +} +``` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/AutoComplete-Select.md b/docs/pt-BR/UI/AspNetCore/AutoComplete-Select.md new file mode 100644 index 0000000000..2fd0da045a --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/AutoComplete-Select.md @@ -0,0 +1,74 @@ +# ASP.NET Core MVC / Razor Pages: Auto-Complete Select +Um componente de seleção simples às vezes não é útil com uma grande quantidade de dados. O ABP fornece uma implementação de seleção que funciona com paginação e pesquisa no lado do servidor usando o [Select2](https://select2.org/). Ele funciona bem com escolhas únicas ou múltiplas. + +Uma captura de tela pode ser mostrada abaixo. + +| Único | Múltiplo | +| --- | --- | +| ![autocomplete-select-example](../../images/abp-select2-single.png) |![autocomplete-select-example](../../images/abp-select2-multiple.png) | + +## Começando + +Esta é uma funcionalidade central e é usada pelo ABP Framework. Não há instalação personalizada ou pacotes adicionais necessários. + +## Uso + +Um uso simples é apresentado abaixo. + +```html +<select asp-for="Book.AuthorId" + class="auto-complete-select" + data-autocomplete-api-url="/api/app/author" + data-autocomplete-display-property="name" + data-autocomplete-value-property="id" + data-autocomplete-items-property="items" + data-autocomplete-filter-param-name="filter" + data-autocomplete-allow-clear="true"> + + <!-- Você pode definir a(s) opção(ões) selecionada(s) aqui --> + <option selected value="@SelectedAuthor.Id">@SelectedAuthor.Name</option> +</select> +``` + +O select deve ter a classe `auto-complete-select` e os seguintes atributos: + +- `data-autocomplete-api-url`: * URL do endpoint da API para obter os itens da seleção. Será enviado uma requisição **GET** para esta URL. +- `data-autocomplete-display-property`: * Nome da propriedade para exibição. _(Por exemplo: `name` ou `title`. Nome da propriedade da entidade/dto.)_. +- `data-autocomplete-value-property`: * Nome da propriedade identificadora. _(Por exemplo: `id`)_. +- `data-autocomplete-items-property`: * Nome da propriedade da coleção no objeto de resposta. _(Por exemplo: `items`)_ +- `data-autocomplete-filter-param-name`: * Nome da propriedade de texto de filtro. _(Por exemplo: `filter`)_. +- `data-autocomplete-selected-item-name`: Texto para exibir como item selecionado. +- `data-autocomplete-parent-selector`: Expressão seletora jQuery para o DOM pai. _(Se estiver em um modal, é sugerido enviar o seletor do modal como este parâmetro)_. +- `data-autocomplete-allow-clear`: Se `true`, permitirá limpar o valor selecionado. Valor padrão: `false`. +- `data-autocomplete-placeholder`: Texto de espaço reservado para exibir quando nenhum valor estiver selecionado. + +Além disso, o(s) valor(es) selecionado(s) deve(m) ser definido(s) com as tags `<option>` dentro do select, uma vez que a paginação é aplicada e as opções selecionadas podem não ter sido carregadas ainda. + + +### Escolhas Múltiplas +O AutoComplete Select suporta escolhas múltiplas. Se a tag select tiver o atributo `multiple`, permitirá escolher várias opções. + +```html +<select asp-for="Book.TagIds" + class="auto-complete-select" + multiple="multiple" + data-autocomplete-api-url="/api/app/tags" + data-autocomplete-display-property="name" + data-autocomplete-value-property="id" + data-autocomplete-items-property="items" + data-autocomplete-filter-param-name="filter"> + @foreach(var tag in SelectedTags) + { + <option selected value="@tag.Id">@tag.Name</option> + } +</select> +``` + +Será automaticamente vinculado a uma coleção do tipo de valor definido. +```csharp + public List<Guid> TagIds { get; set; } +``` + +## Avisos +Se o usuário autenticado não tiver permissão na URL fornecida, o usuário receberá um erro de autorização. Tenha cuidado ao projetar esse tipo de interface de usuário. +Você pode criar um endpoint/método específico, [não autorizado](../../Authorization.md), para obter a lista de itens, para que a página possa recuperar dados de pesquisa de uma entidade dependente sem dar permissão de leitura completa aos usuários. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Basic-Theme.md b/docs/pt-BR/UI/AspNetCore/Basic-Theme.md new file mode 100644 index 0000000000..84874978e6 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Basic-Theme.md @@ -0,0 +1,96 @@ +# ASP.NET Core MVC / Razor Pages: O Tema Básico + +O Tema Básico é uma implementação de tema para a interface do usuário do ASP.NET Core MVC / Razor Pages. É um tema minimalista que não adiciona nenhum estilo além do [Bootstrap](https://getbootstrap.com/) básico. Você pode usar o Tema Básico como o **tema base** e construir seu próprio tema ou estilo em cima dele. Veja a seção *Customização*. + +O Tema Básico possui suporte para idiomas da direita para a esquerda (RTL). + +> Se você está procurando um tema profissional e pronto para uso empresarial, você pode conferir o [Tema Lepton](https://commercial.abp.io/themes), que faz parte do [ABP Commercial](https://commercial.abp.io/). + +> Veja o documento [Theming](Theming.md) para aprender sobre temas. + +## Instalação + +Se você precisa instalar manualmente este tema, siga os passos abaixo: + +* Instale o pacote NuGet [Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic) em seu projeto web. +* Adicione `AbpAspNetCoreMvcUiBasicThemeModule` no atributo `[DependsOn(...)]` para a sua [classe de módulo](../../Module-Development-Basics.md) no projeto web. +* Instale o pacote NPM [@abp/aspnetcore.mvc.ui.theme.basic](https://www.npmjs.com/package/@abp/aspnetcore.mvc.ui.theme.basic) em seu projeto web (por exemplo, `npm install @abp/aspnetcore.mvc.ui.theme.basic` ou `yarn add @abp/aspnetcore.mvc.ui.theme.basic`). +* Execute o comando `abp install-libs` em um terminal de linha de comando na pasta do projeto web. + +## Layouts + +O Tema Básico implementa os layouts padrão. Todos os layouts implementam as seguintes partes: + +* [Bundles](Bundling-Minification.md) globais +* [Alertas de página](Page-Alerts.md) +* [Hooks de layout](Layout-Hooks.md) +* Recursos de [widget](Widgets.md) + +### O Layout da Aplicação + +![basic-theme-application-layout](../../images/basic-theme-application-layout.png) + +O Layout da Aplicação implementa as seguintes partes, além das partes comuns mencionadas acima: + +* Marca +* [Menu](Navigation-Menu.md) principal +* [Toolbar](Toolbars.md) principal com seleção de idioma e menu do usuário + +### O Layout da Conta + +![basic-theme-account-layout](../../images/basic-theme-account-layout.png) + +O Layout da Conta implementa as seguintes partes, além das partes comuns mencionadas acima: + +* Marca +* [Menu](Navigation-Menu.md) principal +* [Toolbar](Toolbars.md) principal com seleção de idioma e menu do usuário +* Área de troca de inquilino + +### Layout Vazio + +O layout vazio é vazio, como o nome sugere. No entanto, ele implementa as partes comuns mencionadas acima. + +## Customização + +Você tem duas opções para personalizar este tema: + +### Sobrescrevendo Estilos/Componentes + +Nesta abordagem, você continua a usar o tema como pacotes NuGet e NPM e personaliza as partes que precisa. Existem várias maneiras de personalizá-lo; + +#### Sobrescrever os Estilos + +1. Crie um arquivo CSS na pasta `wwwroot` do seu projeto: + +![example-global-styles](../../images/example-global-styles.png) + +2. Adicione o arquivo de estilo ao bundle global, no método `ConfigureServices` da sua [classe de módulo](../../Module-Development-Basics.md): + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.StyleBundles.Configure(BasicThemeBundles.Styles.Global, bundle => + { + bundle.AddFiles("/styles/global-styles.css"); + }); +}); +```` + +#### Sobrescrever os Componentes + +Veja o [Guia de Customização da Interface do Usuário](Customization-User-Interface.md) para aprender como substituir componentes, personalizar e estender a interface do usuário. + +### Copiar e Personalizar + +Você pode executar o seguinte comando [ABP CLI](../../CLI.md) no diretório do projeto **Web** para copiar o código-fonte para a sua solução: + +`abp add-module Volo.BasicTheme --with-source-code --add-to-solution-file` + +---- + +Ou, você pode baixar o [código-fonte](https://github.com/abpframework/abp/tree/dev/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic) do Tema Básico, copiar manualmente o conteúdo do projeto para a sua solução, reorganizar as dependências de pacote/módulo (veja a seção de Instalação acima para entender como ele foi instalado no projeto) e personalizar livremente o tema com base nos requisitos da sua aplicação. + +## Veja também + +* [Theming](Theming.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Branding.md b/docs/pt-BR/UI/AspNetCore/Branding.md new file mode 100644 index 0000000000..a6c4c7cd2b --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Branding.md @@ -0,0 +1,45 @@ +# ASP.NET Core MVC / Razor Pages: Branding + +## IBrandingProvider + +`IBrandingProvider` é uma interface simples que é usada para mostrar o nome e o logotipo da aplicação no layout. + +A captura de tela abaixo mostra *MyProject* como o nome da aplicação: + +![branding-nobrand](../../images/branding-nobrand.png) + +Você pode implementar a interface `IBrandingProvider` ou herdar da classe `DefaultBrandingProvider` para definir o nome da aplicação: + +````csharp +using Volo.Abp.Ui.Branding; +using Volo.Abp.DependencyInjection; + +namespace MyProject.Web +{ + [Dependency(ReplaceServices = true)] + public class MyProjectBrandingProvider : DefaultBrandingProvider + { + public override string AppName => "Book Store"; + + public override string LogoUrl => "/logo.png"; + } +} +```` + +> Atualmente, definir o `AppName` só é aplicável ao [Tema Básico](../../Themes/Basic.md), não tem efeito nos outros [temas oficiais](../../Themes/Index.md). + +O resultado será como mostrado abaixo: + +![bookstore-added-logo](../../images/bookstore-added-logo.png) + +`IBrandingProvider` possui as seguintes propriedades: + +* `AppName`: O nome da aplicação. +* `LogoUrl`: Uma URL para mostrar o logotipo da aplicação. +* `LogoReverseUrl`: Uma URL para mostrar o logotipo da aplicação em um tema de cor reversa (escuro, por exemplo). + +> **Dica**: `IBrandingProvider` é usado em cada atualização de página. Para uma aplicação multi-inquilino, você pode retornar um nome de aplicação específico do inquilino para personalizá-lo por inquilino. + +## Sobrescrevendo a Área de Branding + +Você pode consultar o [Guia de Customização de UI](Customization-User-Interface.md) para aprender como substituir a área de branding por um componente de visualização personalizado. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Bundling-Minification.md b/docs/pt-BR/UI/AspNetCore/Bundling-Minification.md new file mode 100644 index 0000000000..3b4a090629 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Bundling-Minification.md @@ -0,0 +1,479 @@ +# ASP.NET Core MVC Bundling & Minification + +Existem várias maneiras de agrupar e minificar recursos do lado do cliente (arquivos JavaScript e CSS). As formas mais comuns são: + +* Usando a extensão do Visual Studio [Bundler & Minifier](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier) ou o [pacote NuGet](https://www.nuget.org/packages/BuildBundlerMinifier/). +* Usando os gerenciadores de tarefas [Gulp](https://gulpjs.com/)/[Grunt](https://gruntjs.com/) e seus plugins. + +O ABP oferece uma maneira simples, dinâmica, poderosa, modular e integrada. + +## Pacote Volo.Abp.AspNetCore.Mvc.UI.Bundling + +> Este pacote já está instalado por padrão nos modelos de inicialização. Portanto, na maioria das vezes, você não precisa instalá-lo manualmente. + +Se você não estiver usando um modelo de inicialização, pode usar o [ABP CLI](../../CLI.md) para instalá-lo em seu projeto. Execute o seguinte comando na pasta que contém o arquivo .csproj do seu projeto: + +```` +abp add-package Volo.Abp.AspNetCore.Mvc.UI.Bundling +```` + +> Se você ainda não o fez, primeiro precisa instalar o [ABP CLI](../../CLI.md). Para outras opções de instalação, consulte [a página de descrição do pacote](https://abp.io/package-detail/Volo.Abp.AspNetCore.Mvc.UI.Bundling). + +## Tag Helpers de Agrupamento Razor + +A maneira mais simples de criar um pacote é usar os tag helpers `abp-script-bundle` ou `abp-style-bundle`. Exemplo: + +````html +<abp-style-bundle name="MyGlobalBundle"> + <abp-style src="/libs/bootstrap/css/bootstrap.css" /> + <abp-style src="/libs/font-awesome/css/font-awesome.css" /> + <abp-style src="/libs/toastr/toastr.css" /> + <abp-style src="/styles/my-global-style.css" /> +</abp-style-bundle> +```` + +Este pacote define um pacote de estilo com um **nome único**: `MyGlobalBundle`. É muito fácil entender como usá-lo. Vamos ver como ele *funciona*: + +* O ABP cria o pacote como **lazy** a partir dos arquivos fornecidos quando é **solicitado pela primeira vez**. Para as chamadas subsequentes, ele é retornado do **cache**. Isso significa que, se você adicionar condicionalmente os arquivos ao pacote, ele será executado apenas uma vez e quaisquer alterações na condição não afetarão o pacote para as próximas solicitações. +* O ABP adiciona os arquivos do pacote **individualmente** à página para o ambiente `development`. Ele agrupa e minifica automaticamente para outros ambientes (`staging`, `production`...). Consulte a seção *Modo de Agrupamento* para alterar esse comportamento. +* Os arquivos do pacote podem ser arquivos **físicos** ou [**virtuais/embutidos**](../../Virtual-File-System.md). +* O ABP adiciona automaticamente uma **string de consulta de versão** ao URL do arquivo do pacote para evitar que os navegadores armazenem em cache quando o pacote está sendo atualizado. (como ?_v=67872834243042 - gerado a partir da última data de alteração dos arquivos relacionados). A versão funciona mesmo se os arquivos do pacote forem adicionados individualmente à página (no ambiente de desenvolvimento). + +### Importando os Tag Helpers de Agrupamento + +> Isso já é importado por padrão nos modelos de inicialização. Portanto, na maioria das vezes, você não precisa adicioná-lo manualmente. + +Para usar os tag helpers de pacote, você precisa adicioná-los ao seu arquivo `_ViewImports.cshtml` ou à sua página: + +```` +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling +```` + +### Pacotes sem nome + +O nome é **opcional** para os tag helpers de pacote razor. Se você não definir um nome, ele será **calculado** automaticamente com base nos nomes dos arquivos do pacote usados (eles são **concatenados** e **hashados**). Exemplo: + +````html +<abp-style-bundle> + <abp-style src="/libs/bootstrap/css/bootstrap.css" /> + <abp-style src="/libs/font-awesome/css/font-awesome.css" /> + <abp-style src="/libs/toastr/toastr.css" /> + @if (ViewBag.IncludeCustomStyles != false) + { + <abp-style src="/styles/my-global-style.css" /> + } +</abp-style-bundle> +```` + +Isso pode criar **dois pacotes diferentes** (um inclui o `my-global-style.css` e o outro não). + +Vantagens de pacotes **sem nome**: + +* É possível **adicionar itens condicionalmente** ao pacote. Mas isso pode levar a várias variações do pacote com base nas condições. + +Vantagens de pacotes **com nome**: + +* Outros **módulos podem contribuir** para o pacote pelo nome (consulte as seções abaixo). + +### Arquivo Único + +Se você precisa apenas adicionar um único arquivo à página, pode usar a tag `abp-script` ou `abp-style` sem envolvê-la na tag `abp-script-bundle` ou `abp-style-bundle`. Exemplo: + +````xml +<abp-script src="/scripts/my-script.js" /> +```` + +O nome do pacote será *scripts.my-scripts* para o exemplo acima ("/" é substituído por "."). Todos os recursos de agrupamento funcionam como esperado para pacotes de arquivo único também. + +## Opções de Agrupamento + +Se você precisa usar o mesmo pacote em **múltiplas páginas** ou deseja usar alguns recursos mais **poderosos**, você pode configurar pacotes **por código** em sua classe de [módulo](../../Module-Development-Basics.md). + +### Criando um Novo Pacote + +Exemplo de uso: + +````C# +[DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))] +public class MyWebModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure<AbpBundlingOptions>(options => + { + options + .ScriptBundles + .Add("MyGlobalBundle", bundle => { + bundle.AddFiles( + "/libs/jquery/jquery.js", + "/libs/bootstrap/js/bootstrap.js", + "/libs/toastr/toastr.min.js", + "/scripts/my-global-scripts.js" + ); + }); + }); + } +} +```` + +> Você pode usar o mesmo nome (*MyGlobalBundle* aqui) para um pacote de script e estilo, pois eles são adicionados a coleções diferentes (`ScriptBundles` e `StyleBundles`). + +Após definir um pacote desse tipo, ele pode ser incluído em uma página usando os mesmos tag helpers definidos acima. Exemplo: + +````html +<abp-script-bundle name="MyGlobalBundle" /> +```` + +Desta vez, nenhum arquivo é definido na definição do tag helper porque os arquivos do pacote são definidos pelo código. + +### Configurando um Pacote Existente + +O ABP também oferece suporte à [modularidade](../../Module-Development-Basics.md) para agrupamento. Um módulo pode modificar um pacote existente criado por um módulo dependente. Exemplo: + +````C# +[DependsOn(typeof(MyWebModule))] +public class MyWebExtensionModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure<AbpBundlingOptions>(options => + { + options + .ScriptBundles + .Configure("MyGlobalBundle", bundle => { + bundle.AddFiles( + "/scripts/my-extension-script.js" + ); + }); + }); + } +} +```` + +Você também pode usar o método `ConfigureAll` para configurar todos os pacotes existentes: + +````C# +[DependsOn(typeof(MyWebModule))] +public class MyWebExtensionModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure<AbpBundlingOptions>(options => + { + options + .ScriptBundles + .ConfigureAll(bundle => { + bundle.AddFiles( + "/scripts/my-extension-script.js" + ); + }); + }); + } +} +```` + +## Contribuidores de Pacotes + +Adicionar arquivos a um pacote existente é útil. E se você precisar **substituir** um arquivo no pacote ou quiser adicionar arquivos **condicionalmente**? Definir um contribuidor de pacote fornece poder extra para esses casos. + +Um exemplo de contribuidor de pacote que substitui o bootstrap.css por uma versão personalizada: + +````C# +public class MyExtensionGlobalStyleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.ReplaceOne( + "/libs/bootstrap/css/bootstrap.css", + "/styles/extensions/bootstrap-customized.css" + ); + } +} +```` + +Em seguida, você pode usar esse contribuidor da seguinte maneira: + +````C# +services.Configure<AbpBundlingOptions>(options => +{ + options + .ScriptBundles + .Configure("MyGlobalBundle", bundle => { + bundle.AddContributors(typeof(MyExtensionGlobalStyleContributor)); + }); +}); +```` + +> Você também pode adicionar contribuidores ao criar um novo pacote. + +Os contribuidores também podem ser usados nos tag helpers de pacote. Exemplo: + +````xml +<abp-style-bundle> + <abp-style type="@typeof(BootstrapStyleContributor)" /> + <abp-style src="/libs/font-awesome/css/font-awesome.css" /> + <abp-style src="/libs/toastr/toastr.css" /> +</abp-style-bundle> +```` + +As tags `abp-style` e `abp-script` podem receber atributos `type` (em vez de atributos `src`) como mostrado neste exemplo. Quando você adiciona um contribuidor de pacote, suas dependências também são adicionadas automaticamente ao pacote. + +### Dependências de Contribuidores + +Um contribuidor de pacote pode ter uma ou mais dependências de outros contribuidores. +Exemplo: + +````C# +[DependsOn(typeof(MyDependedBundleContributor))] //Define a dependência +public class MyExtensionStyleBundleContributor : BundleContributor +{ + //... +} +```` + +Quando um contribuidor de pacote é adicionado, suas dependências são adicionadas **automaticamente e recursivamente**. As dependências são adicionadas pela **ordem de dependência** evitando **duplicatas**. As duplicatas são evitadas mesmo que estejam em pacotes separados. O ABP organiza todos os pacotes em uma página e elimina duplicações. + +Criar contribuidores e definir dependências é uma maneira de organizar a criação de pacotes em diferentes módulos. + +### Extensões de Contribuidores + +Em alguns cenários avançados, você pode querer fazer alguma configuração adicional sempre que um contribuidor de pacote for usado. As extensões de contribuidor funcionam perfeitamente quando o contribuidor estendido é usado. + +O exemplo abaixo adiciona alguns estilos para a biblioteca prism.js: + +````csharp +public class MyPrismjsStyleExtension : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/libs/prismjs/plugins/toolbar/prism-toolbar.css"); + } +} +```` + +Em seguida, você pode configurar `AbpBundleContributorOptions` para estender o `PrismjsStyleBundleContributor` existente. + +````csharp +Configure<AbpBundleContributorOptions>(options => +{ + options + .Extensions<PrismjsStyleBundleContributor>() + .Add<MyPrismjsStyleExtension>(); +}); +```` + +Sempre que `PrismjsStyleBundleContributor` for adicionado a um pacote, `MyPrismjsStyleExtension` também será adicionado automaticamente. + +### Acessando o IServiceProvider + +Embora raramente seja necessário, `BundleConfigurationContext` possui uma propriedade `ServiceProvider` que permite resolver dependências de serviço dentro do método `ConfigureBundle`. + +### Contribuidores de Pacotes Padrão + +Adicionar um recurso específico do pacote NPM (arquivos js, css) a um pacote é bastante simples para esse pacote. Por exemplo, você sempre adiciona o arquivo `bootstrap.css` para o pacote NPM do bootstrap. + +Existem contribuidores embutidos para todos os [pacotes NPM padrão](Client-Side-Package-Management.md). Por exemplo, se seu contribuidor depende do bootstrap, você pode apenas declará-lo, em vez de adicionar o bootstrap.css você mesmo. + +````C# +[DependsOn(typeof(BootstrapStyleContributor))] //Define a dependência de estilo do bootstrap +public class MyExtensionStyleBundleContributor : BundleContributor +{ + //... +} +```` + +Usando os contribuidores embutidos para pacotes padrão; + +* Impede que você digite **os caminhos de recursos inválidos**. +* Impede a alteração do seu contribuidor se o **caminho do recurso mudar** (o contribuidor dependente lidará com isso). +* Impede que vários módulos adicionem **arquivos duplicados**. +* Gerencia **dependências recursivamente** (adiciona dependências de dependências, se necessário). + +#### Pacote Volo.Abp.AspNetCore.Mvc.UI.Packages + +> Este pacote já está instalado por padrão nos modelos de inicialização. Portanto, na maioria das vezes, você não precisa instalá-lo manualmente. + +Se você não estiver usando um modelo de inicialização, pode usar o [ABP CLI](../../CLI.md) para instalá-lo em seu projeto. Execute o seguinte comando na pasta que contém o arquivo .csproj do seu projeto: + +```` +abp add-package Volo.Abp.AspNetCore.Mvc.UI.Packages +```` + +> Se você ainda não o fez, primeiro precisa instalar o [ABP CLI](../../CLI.md). Para outras opções de instalação, consulte [a página de descrição do pacote](https://abp.io/package-detail/Volo.Abp.AspNetCore.Mvc.UI.Packages). + +### Herança de Pacotes + +Em alguns casos específicos, pode ser necessário criar um **novo** pacote **herdado** de outro(s) pacote(s). Ao herdar de um pacote (recursivamente), todos os arquivos/contribuidores desse pacote são herdados. Em seguida, o pacote derivado pode adicionar ou modificar arquivos/contribuidores **sem modificar** o pacote original. +Exemplo: + +````c# +services.Configure<AbpBundlingOptions>(options => +{ + options + .StyleBundles + .Add("MyTheme.MyGlobalBundle", bundle => { + bundle + .AddBaseBundles("MyGlobalBundle") //Pode adicionar vários + .AddFiles( + "/styles/mytheme-global-styles.css" + ); + }); +}); +```` + +## Opções Adicionais + +Esta seção mostra outras opções úteis para o sistema de agrupamento e minificação. + +### Modo de Agrupamento + +O ABP adiciona arquivos de pacote individualmente à página para o ambiente `development`. Ele agrupa e minifica automaticamente para outros ambientes (`staging`, `production`...). Na maioria das vezes, esse é o comportamento desejado. No entanto, você pode configurá-lo manualmente em alguns casos. Existem quatro modos; + +* `Auto`: Determina automaticamente o modo com base no ambiente. +* `None`: Sem agrupamento ou minificação. +* `Bundle`: Agrupado, mas não minificado. +* `BundleAndMinify`: Agrupado e minificado. + +Você pode configurar `AbpBundlingOptions` no `ConfigureServices` do seu [módulo](../../Module-Development-Basics.md). + +**Exemplo:** + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.Mode = BundlingMode.Bundle; +}); +```` + +### Ignorar para Minificação + +É possível ignorar um arquivo específico para a minificação. + +**Exemplo:** + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.MinificationIgnoredFiles.Add("/scripts/myscript.js"); +}); +```` + +O arquivo fornecido ainda é adicionado ao pacote, mas não é minificado neste caso. + +### Carregar JavaScript e CSS de forma assíncrona + +Você pode configurar `AbpBundlingOptions` para carregar todos ou um único arquivo js/css de forma assíncrona. + +**Exemplo:** + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.PreloadStyles.Add("/__bundles/Basic.Global"); + options.DeferScriptsByDefault = true; +}); +```` + +**HTML de saída:** +````html +<link rel="preload" href="/__bundles/Basic.Global.F4FA61F368098407A4C972D0A6914137.css?_v=637697363694828051" as="style" onload="this.rel='stylesheet'"/> + +<script defer src="/libs/timeago/locales/jquery.timeago.en.js?_v=637674729040000000"></script> +```` + +### Suporte a Arquivos Externos/CDN + +O sistema de agrupamento reconhece automaticamente os arquivos externos/CDN e os adiciona à página sem nenhuma alteração. + +#### Usando Arquivos Externos/CDN em `AbpBundlingOptions` + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.StyleBundles + .Add("MyStyleBundle", configuration => + { + configuration + .AddFiles("/styles/my-style1.css") + .AddFiles("/styles/my-style2.css") + .AddFiles("https://cdn.abp.io/bootstrap.css") + .AddFiles("/styles/my-style3.css") + .AddFiles("/styles/my-style4.css"); + }); + + options.ScriptBundles + .Add("MyScriptBundle", configuration => + { + configuration + .AddFiles("/scripts/my-script1.js") + .AddFiles("/scripts/my-script2.js") + .AddFiles("https://cdn.abp.io/bootstrap.js") + .AddFiles("/scripts/my-script3.js") + .AddFiles("/scripts/my-script4.js"); + }); +}); +```` + +**HTML de saída:** + +````html +<link rel="stylesheet" href="/__bundles/MyStyleBundle.EA8C28419DCA43363E9670973D4C0D15.css?_v=638331889644609730" /> +<link rel="stylesheet" href="https://cdn.abp.io/bootstrap.css" /> +<link rel="stylesheet" href="/__bundles/MyStyleBundle.AC2E0AA6C461A0949A1295E9BDAC049C.css?_v=638331889644623860" /> + +<script src="/__bundles/MyScriptBundle.C993366DF8840E08228F3EE685CB08E8.js?_v=638331889644937120"></script> +<script src="https://cdn.abp.io/bootstrap.js"></script> +<script src="/__bundles/MyScriptBundle.2E8D0FDC6334D2A6B847393A801525B7.js?_v=638331889644943970"></script> +```` + +#### Usando Arquivos Externos/CDN em Tag Helpers. + +````html +<abp-style-bundle name="MyStyleBundle"> + <abp-style src="/styles/my-style1.css" /> + <abp-style src="/styles/my-style2.css" /> + <abp-style src="https://cdn.abp.io/bootstrap.css" /> + <abp-style src="/styles/my-style3.css" /> + <abp-style src="/styles/my-style4.css" /> +</abp-style-bundle> + +<abp-script-bundle name="MyScriptBundle"> + <abp-script src="/scripts/my-script1.js" /> + <abp-script src="/scripts/my-script2.js" /> + <abp-script src="https://cdn.abp.io/bootstrap.js" /> + <abp-script src="/scripts/my-script3.js" /> + <abp-script src="/scripts/my-script4.js" /> +</abp-script-bundle> +```` + +**HTML de saída:** + +````html +<link rel="stylesheet" href="/__bundles/MyStyleBundle.C60C7B9C1F539659623BB6E7227A7C45.css?_v=638331889645002500" /> +<link rel="stylesheet" href="https://cdn.abp.io/bootstrap.css" /> +<link rel="stylesheet" href="/__bundles/MyStyleBundle.464328A06039091534650B0E049904C6.css?_v=638331889645012300" /> + +<script src="/__bundles/MyScriptBundle.55FDCBF2DCB9E0767AE6FA7487594106.js?_v=638331889645050410"></script> +<script src="https://cdn.abp.io/bootstrap.js"></script> +<script src="/__bundles/MyScriptBundle.191CB68AB4F41C8BF3A7AE422F19A3D2.js?_v=638331889645055490"></script> +```` + +## Temas + +Os temas usam os contribuidores de pacotes padrão para adicionar recursos de biblioteca aos layouts de página. Os temas também podem definir alguns pacotes padrão/globais, para que qualquer módulo possa contribuir para esses pacotes padrão/globais. Consulte a documentação de [temas](Theming.md) para mais informações. + +## Melhores Práticas e Sugestões + +É sugerido definir vários pacotes para uma aplicação, cada um usado para diferentes propósitos. + +* **Pacote global**: Pacotes de estilo/script globais são incluídos em todas as páginas da aplicação. Os temas já definem pacotes de estilo e script globais. Seu módulo pode contribuir para eles. +* **Pacotes de layout**: Este é um pacote específico para um layout individual. Contém apenas recursos compartilhados entre todas as páginas que usam o layout. Use os tag helpers de agrupamento para criar o pacote como uma boa prática. +* **Pacotes de módulo**: Para recursos compartilhados entre as páginas de um módulo individual. +* **Pacotes de página**: Pacotes específicos criados para cada página. Use os tag helpers de agrupamento para criar o pacote como uma melhor prática. + +Estabeleça um equilíbrio entre desempenho, uso de largura de banda da rede e quantidade de pacotes. + +## Veja também + +* [Gerenciamento de Pacotes do Lado do Cliente](Client-Side-Package-Management.md) +* [Temas](Theming.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Client-Side-Package-Management.md b/docs/pt-BR/UI/AspNetCore/Client-Side-Package-Management.md new file mode 100644 index 0000000000..0519396727 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Client-Side-Package-Management.md @@ -0,0 +1,115 @@ +## Gerenciamento de Pacotes do Lado do Cliente no ASP.NET Core MVC + +O framework ABP pode funcionar com qualquer tipo de sistema de gerenciamento de pacotes do lado do cliente. Você até pode decidir não usar nenhum sistema de gerenciamento de pacotes e gerenciar suas dependências manualmente. + +No entanto, o framework ABP funciona melhor com o **NPM/Yarn**. Por padrão, os módulos integrados são configurados para funcionar com o NPM/Yarn. + +Por fim, sugerimos o [**Yarn**](https://classic.yarnpkg.com/) em vez do NPM, pois ele é mais rápido, estável e também compatível com o NPM. + +### Pacotes NPM do ABP + +O ABP é uma plataforma modular. Todo desenvolvedor pode criar módulos e os módulos devem funcionar juntos em um estado **compatível** e **estável**. + +Um desafio são as **versões dos pacotes NPM dependentes**. E se dois módulos diferentes usarem a mesma biblioteca JavaScript, mas em versões diferentes (e potencialmente incompatíveis)? + +Para resolver o problema de versionamento, criamos um **conjunto padrão de pacotes** que dependem de algumas bibliotecas de terceiros comuns. Alguns exemplos de pacotes são [@abp/jquery](https://www.npmjs.com/package/@abp/jquery), [@abp/bootstrap](https://www.npmjs.com/package/@abp/bootstrap) e [@abp/font-awesome](https://www.npmjs.com/package/@abp/font-awesome). Você pode ver a **lista de pacotes** no [repositório do GitHub](https://github.com/volosoft/abp/tree/master/npm/packs). + +A vantagem de um **pacote padrão** é: + +* Ele depende de uma **versão padrão** de um pacote. Depender desse pacote é **seguro** porque todos os módulos dependem da mesma versão. +* Ele contém os mapeamentos para copiar os recursos da biblioteca (arquivos js, css, img...) da pasta `node_modules` para a pasta `wwwroot/libs`. Consulte a seção *Mapeando os Recursos da Biblioteca* para mais informações. + +Depender de um pacote padrão é fácil. Basta adicioná-lo ao seu arquivo **package.json** como você normalmente faria. Exemplo: + +```json +{ + ... + "dependencies": { + "@abp/bootstrap": "^1.0.0" + } +} +``` + +É sugerido depender de um pacote padrão em vez de depender diretamente de um pacote de terceiros. + +#### Instalação do Pacote + +Depois de depender de um pacote NPM, tudo o que você precisa fazer é executar o comando **yarn** no terminal para instalar todos os pacotes e suas dependências: + +```bash +yarn +``` + +Alternativamente, você pode usar `npm install`, mas o [Yarn](https://classic.yarnpkg.com/) é sugerido como mencionado anteriormente. + +#### Contribuição de Pacotes + +Se você precisar de um pacote NPM de terceiros que não esteja no conjunto padrão de pacotes, você pode criar uma Pull Request no repositório do Github [repositório](https://github.com/volosoft/abp). Uma Pull Request que segue essas regras é aceita: + +* O nome do pacote deve ser `@abp/nome-do-pacote` para um `nome-do-pacote` no NPM (exemplo: `@abp/bootstrap` para o pacote `bootstrap`). +* Deve ser a versão **mais recente estável** do pacote. +* Deve depender apenas de um pacote de terceiros. Pode depender de vários pacotes `@abp/*`. +* O pacote deve incluir um arquivo `abp.resourcemapping.js` formatado como definido na seção *Mapeando os Recursos da Biblioteca*. Este arquivo deve mapear apenas os recursos do pacote dependente. +* Você também precisa criar [contribuidor(es) de pacote](Bundling-Minification.md) para o pacote que você criou. + +Veja os pacotes padrão atuais para exemplos. + +### Mapeando os Recursos da Biblioteca + +O uso de pacotes NPM e da ferramenta NPM/Yarn é o padrão de fato para bibliotecas do lado do cliente. A ferramenta NPM/Yarn cria uma pasta **node_modules** na pasta raiz do seu projeto da web. + +O próximo desafio é copiar os recursos necessários (arquivos js, css, img...) da pasta `node_modules` para uma pasta dentro da pasta **wwwroot** para torná-los acessíveis aos clientes/navegadores. + +O comando `abp install-libs` da CLI do ABP **copia os recursos** da pasta **node_modules** para a pasta **wwwroot/libs**. Cada **pacote padrão** (consulte a seção *@ABP NPM Packages*) define o mapeamento para seus próprios arquivos. Portanto, na maioria das vezes, você só precisa configurar as dependências. + +Os modelos de inicialização já estão configurados para funcionar com tudo isso. Esta seção explicará as opções de configuração. + +#### Arquivo de Definição de Mapeamento de Recursos + +Um módulo deve definir um arquivo JavaScript chamado `abp.resourcemapping.js` formatado como no exemplo abaixo: + +```json +module.exports = { + aliases: { + "@node_modules": "./node_modules", + "@libs": "./wwwroot/libs" + }, + clean: [ + "@libs", + "!@libs/**/foo.txt" + ], + mappings: { + + } +} +``` + +* A seção **aliases** define aliases padrão (placeholders) que podem ser usados nos caminhos de mapeamento. **@node_modules** e **@libs** são obrigatórios (pelos pacotes padrão), você pode definir seus próprios aliases para reduzir a duplicação. +* A seção **clean** é uma lista de pastas a serem limpas antes de copiar os arquivos. A correspondência de glob e a negação estão habilitadas, então você pode ajustar o que excluir e manter. O exemplo acima limpará tudo dentro de `./wwwroot/libs`, mas manterá quaisquer arquivos `foo.txt`. +* A seção **mappings** é uma lista de mapeamentos de arquivos/pastas a serem copiados. Este exemplo não copia nenhum recurso em si, mas depende de um pacote padrão. + +Um exemplo de configuração de mapeamento é mostrado abaixo: + +```json +mappings: { + "@node_modules/bootstrap/dist/css/bootstrap.css": "@libs/bootstrap/css/", + "@node_modules/bootstrap/dist/js/bootstrap.bundle.js": "@libs/bootstrap/js/", + "@node_modules/bootstrap-datepicker/dist/locales/*.*": "@libs/bootstrap-datepicker/locales/", + "@node_modules/bootstrap-v4-rtl/dist/**/*": "@libs/bootstrap-v4-rtl/dist/" +} +``` + +#### Comando install-libs + +Depois de configurar corretamente o arquivo `abp.resourcemapping.js`, você pode executar o seguinte comando da CLI do ABP no terminal: + +````bash +abp install-libs +```` + +Quando você executar este comando, todos os pacotes copiarão seus próprios recursos para a pasta `wwwroot/libs`. Executar `abp install-libs` é necessário apenas se você fizer uma alteração em suas dependências no arquivo **package.json**. + +#### Veja também + +* [Empacotamento e Minificação](Bundling-Minification.md) +* [Tematização](Theming.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Customization-User-Interface.md b/docs/pt-BR/UI/AspNetCore/Customization-User-Interface.md new file mode 100644 index 0000000000..f7cdf53461 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Customization-User-Interface.md @@ -0,0 +1,477 @@ +# Guia de Customização da Interface do Usuário do ASP.NET Core (MVC / Razor Pages) + +Este documento explica como substituir a interface do usuário de um módulo de aplicativo dependente [módulo de aplicativo](../../Modules/Index.md) ou [tema](Theming.md) para aplicativos ASP.NET Core MVC / Razor Page. + +## Substituindo uma Página + +Esta seção aborda o desenvolvimento de [Razor Pages](https://docs.microsoft.com/en-us/aspnet/core/razor-pages/), que é a abordagem recomendada para criar interfaces de usuário renderizadas no servidor para o ASP.NET Core. Os módulos pré-construídos geralmente usam a abordagem Razor Pages em vez do padrão clássico MVC (as próximas seções também abordarão o padrão MVC). + +Normalmente, você tem três tipos de requisitos de substituição para uma página: + +* Substituir apenas o lado do **Modelo de Página** (C#) para executar lógica adicional sem alterar a interface do usuário da página. +* Substituir apenas a **Página Razor** (arquivo .chtml) para alterar a interface do usuário sem alterar o código C# por trás da página. +* **Substituir completamente** a página. + +### Substituindo um Modelo de Página (C#) + +````csharp +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Identity; +using Volo.Abp.Identity.Web.Pages.Identity.Users; + +namespace Acme.BookStore.Web.Pages.Identity.Users +{ + [Dependency(ReplaceServices = true)] + [ExposeServices(typeof(EditModalModel))] + public class MyEditModalModel : EditModalModel + { + public MyEditModalModel( + IIdentityUserAppService identityUserAppService, + IIdentityRoleAppService identityRoleAppService + ) : base( + identityUserAppService, + identityRoleAppService) + { + } + + public async override Task<IActionResult> OnPostAsync() + { + //TODO: Lógica adicional + await base.OnPostAsync(); + //TODO: Lógica adicional + } + } +} +```` + +* Esta classe herda e substitui o `EditModalModel` para os usuários e substitui o método `OnPostAsync` para executar lógica adicional antes e depois do código subjacente. +* Ele usa os atributos `ExposeServices` e `Dependency` para substituir a classe. + +### Substituindo uma Página Razor (.CSHTML) + +Substituir um arquivo `.cshtml` (página razor, visualização razor, componente de visualização... etc.) é possível criando o mesmo arquivo `.cshtml` no mesmo caminho. + +#### Exemplo + +Este exemplo substitui a interface do usuário da **página de login** definida pelo [Módulo de Conta](../../Modules/Account.md). + +O módulo de conta define um arquivo `Login.cshtml` na pasta `Pages/Account`. Portanto, você pode substituí-lo criando um arquivo no mesmo caminho: + +![substituindo-login-cshtml](../../images/overriding-login-cshtml.png) + +Normalmente, você deseja copiar o arquivo `.cshtml` original do módulo e fazer as alterações necessárias. Você pode encontrar o arquivo original [aqui](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml). Não copie o arquivo `Login.cshtml.cs`, que é o arquivo de código por trás da página razor e não queremos substituí-lo ainda (veja a próxima seção). + +> Não se esqueça de adicionar [_ViewImports.cshtml](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-7.0#importing-shared-directives) se a página que você deseja substituir contiver [ABP Tag Helpers](../AspNetCore/Tag-Helpers/Index.md). + +````csharp +@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 +```` + +Isso é tudo, você pode alterar o conteúdo do arquivo como desejar. + +### Substituindo Completamente uma Página Razor + +Você pode querer substituir completamente uma página; a página razor e o arquivo C# relacionado à página. + +Nesse caso; + +1. Substitua a classe do modelo de página C# como descrito acima, mas não substitua a classe de modelo de página existente. +2. Substitua a Página Razor conforme descrito acima, mas também altere a diretiva @model para apontar para o novo modelo de página. + +#### Exemplo + +Este exemplo substitui a **página de login** definida pelo [Módulo de Conta](../../Modules/Account.md). + +Crie uma classe de modelo de página derivada de `LoginModel` (definida no namespace `Volo.Abp.Account.Web.Pages.Account`): + +````csharp +public class MyLoginModel : LoginModel +{ + public MyLoginModel( + IAuthenticationSchemeProvider schemeProvider, + IOptions<AbpAccountOptions> accountOptions + ) : base( + schemeProvider, + accountOptions) + { + + } + + public override Task<IActionResult> OnPostAsync(string action) + { + //TODO: Adicionar lógica + return base.OnPostAsync(action); + } + + //TODO: Adicionar novos métodos e propriedades... +} +```` + +Você pode substituir qualquer método ou adicionar novas propriedades/métodos, se necessário. + +> Observe que não usamos `[Dependency(ReplaceServices = true)]` ou `[ExposeServices(typeof(LoginModel))]` porque não queremos substituir a classe existente na injeção de dependência, definimos uma nova. + +Copie o arquivo `Login.cshtml` para a sua solução conforme descrito acima. Altere a diretiva **@model** para apontar para o `MyLoginModel`: + +````xml +@page +... +@model Acme.BookStore.Web.Pages.Account.MyLoginModel +... +```` + +Isso é tudo! Faça qualquer alteração na visualização e execute seu aplicativo. + +#### Substituindo o Modelo de Página Sem Herança + +Você não precisa herdar da classe de modelo de página original (como feito no exemplo anterior). Em vez disso, você pode **reimplementar completamente** a página você mesmo. Nesse caso, basta derivar de `PageModel`, `AbpPageModel` ou qualquer classe base adequada que você precise. + +## Substituindo um Componente de Visualização + +O ABP Framework, temas pré-construídos e módulos definem alguns **componentes de visualização reutilizáveis**. Esses componentes de visualização podem ser substituídos da mesma forma que uma página descrita acima. + +### Exemplo + +A captura de tela abaixo foi tirada do [Tema Básico](Basic-Theme.md) fornecido com o modelo de inicialização do aplicativo. + +![bookstore-brand-area-highlighted](../../images/bookstore-brand-area-highlighted.png) + +O [Tema Básico](Basic-Theme.md) define alguns componentes de visualização para o layout. Por exemplo, a área destacada com o retângulo vermelho acima é chamada de **componente de marca**. Você provavelmente deseja personalizar esse componente adicionando seu **próprio logotipo do aplicativo**. Vamos ver como fazer isso. + +Primeiro, crie seu logotipo e coloque-o em uma pasta em seu aplicativo da web. Usamos o caminho `wwwroot/logos/bookstore-logo.png`. Em seguida, copie a visualização do componente de marca ([aqui](https://github.com/abpframework/abp/blob/dev/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/Default.cshtml)) dos arquivos do tema básico na pasta `Themes/Basic/Components/Brand`. O resultado deve ser semelhante à imagem abaixo: + +![bookstore-added-brand-files](../../images/bookstore-added-brand-files.png) + +Em seguida, altere o `Default.cshtml` como desejar. O conteúdo de exemplo pode ser assim: + +````xml +<a href="/"> + <img src="~/logos/bookstore-logo.png" width="250" height="60"/> +</a> +```` + +Agora, você pode executar o aplicativo para ver o resultado: + +![bookstore-added-logo](../../images/bookstore-added-logo.png) + +Se necessário, você também pode substituir [o arquivo c# de código por trás](https://github.com/abpframework/abp/blob/dev/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/MainNavbarBrandViewComponent.cs) do componente usando o sistema de injeção de dependência. + +### Substituindo o Tema + +Assim como explicado acima, você pode substituir qualquer componente, layout ou classe c# do tema usado. Consulte o documento [tematização](Theming.md) para obter mais informações sobre o sistema de tematização. + +## Substituindo Recursos Estáticos + +Substituir um recurso estático incorporado (como arquivos JavaScript, CSS ou de imagem) de um módulo é bastante fácil. Basta colocar um arquivo no mesmo caminho em sua solução e deixar o [Sistema de Arquivos Virtual](../../Virtual-File-System.md) lidar com ele. + +## Manipulando os Pacotes + +O sistema de [Empacotamento e Minificação](Bundling-Minification.md) fornece um sistema **extensível e dinâmico** para criar pacotes de **scripts** e **estilos**. Ele permite que você estenda e manipule os pacotes existentes. + +### Exemplo: Adicionar um Arquivo CSS Global + +Por exemplo, o ABP Framework define um **pacote de estilo global** que é adicionado a todas as páginas (na verdade, adicionado ao layout pelos temas). Vamos adicionar um **arquivo de estilo personalizado** ao final dos arquivos do pacote, para que possamos substituir qualquer estilo global. + +Primeiro, crie um arquivo CSS e coloque-o em uma pasta dentro do `wwwroot`: + +![bookstore-global-css-file](../../images/bookstore-global-css-file.png) + +Defina algumas regras CSS personalizadas dentro do arquivo. Exemplo: + +````css +.card-title { + color: orange; + font-size: 2em; + text-decoration: underline; +} + +.btn-primary { + background-color: red; +} +```` + +Em seguida, adicione este arquivo ao pacote global de estilo padrão no método `ConfigureServices` do seu [módulo](../../Module-Development-Basics.md): + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.StyleBundles.Configure( + StandardBundles.Styles.Global, //O nome do pacote! + bundleConfiguration => + { + bundleConfiguration.AddFiles("/styles/my-global-styles.css"); + } + ); +}); +```` + +#### O Pacote de Script Global + +Assim como o `StandardBundles.Styles.Global`, há um `StandardBundles.Scripts.Global` que você pode adicionar arquivos ou manipular os existentes. + +### Exemplo: Manipular os Arquivos do Pacote + +O exemplo acima adiciona um novo arquivo ao pacote. Você pode fazer mais se criar uma classe de **contribuinte de pacote**. Exemplo: + +````csharp +public class MyGlobalStyleBundleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.Clear(); + context.Files.Add("/styles/my-global-styles.css"); + } +} +```` + +Em seguida, você pode adicionar o contribuinte a um pacote existente: + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.StyleBundles.Configure( + StandardBundles.Styles.Global, + bundleConfiguration => + { + bundleConfiguration.AddContributors(typeof(MyGlobalStyleBundleContributor)); + } + ); +}); +```` + +Não é uma boa ideia limpar todos os arquivos CSS. Em um cenário do mundo real, você pode encontrar e substituir um arquivo específico pelo seu próprio arquivo. + +### Exemplo: Adicionar um Arquivo JavaScript para uma Página Específica + +Os exemplos acima funcionam com o pacote global adicionado ao layout. E se você quiser adicionar um arquivo CSS/JavaScript (ou substituir um arquivo) para uma página específica definida em um módulo dependente? + +Suponha que você queira executar um código **JavaScript** assim que o usuário entrar na página de **Gerenciamento de Funções** do Módulo de Identidade. + +Primeiro, crie um arquivo JavaScript padrão no `wwwroot`, `Pages` ou `Views` (o ABP suporta adicionar recursos estáticos dentro dessas pastas por padrão). Preferimos a pasta `Pages/Identity/Roles` para seguir as convenções: + +![bookstore-added-role-js-file](../../images/bookstore-added-role-js-file.png) + +O conteúdo do arquivo é simples: + +````js +$(function() { + abp.log.info('Meu arquivo de script de função personalizado foi carregado!'); +}); +```` + +Em seguida, adicione este arquivo ao pacote da página de gerenciamento de funções: + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.ScriptBundles + .Configure( + typeof(Volo.Abp.Identity.Web.Pages.Identity.Roles.IndexModel).FullName, + bundleConfig => + { + bundleConfig.AddFiles("/Pages/Identity/Roles/my-role-script.js"); + }); +}); +```` + +`typeof(Volo.Abp.Identity.Web.Pages.Identity.Roles.IndexModel).FullName` é a maneira segura de obter o nome do pacote para a página de gerenciamento de funções. + +> Observe que nem todas as páginas definem esses pacotes de página. Eles definem apenas se necessário. + +Além de adicionar um novo arquivo CSS/JavaScript a uma página, você também pode substituir o existente (definindo um contribuinte de pacote). + +## Customização do Layout + +Os layouts são definidos pelo tema ([consulte a documentação de tematização](Theming.md)) por design. Eles não estão incluídos em uma solução de aplicativo baixada. Dessa forma, você pode facilmente **atualizar** o tema e obter novos recursos. Você não pode **alterar diretamente** o código do layout em seu aplicativo, a menos que o substitua por seu próprio layout (será explicado nas próximas seções). + +Existem algumas maneiras comuns de **personalizar o layout** descritas nas seções a seguir. + +### Contribuintes de Menu + +Existem dois **menus padrão** definidos pelo ABP Framework: + +![bookstore-menus-highlighted](../../images/bookstore-menus-highlighted.png) + +* `StandardMenus.Main`: O menu principal do aplicativo. +* `StandardMenus.User`: O menu do usuário (geralmente no canto superior direito da tela). + +A renderização dos menus é de responsabilidade do tema, mas os **itens do menu** são determinados pelos módulos e pelo código do seu aplicativo. Basta implementar a interface `IMenuContributor` e **manipular os itens do menu** no método `ConfigureMenuAsync`. + +Os contribuintes de menu são executados sempre que precisam renderizar o menu. Já existe um contribuinte de menu definido no **modelo de inicialização do aplicativo**, para que você possa usá-lo como exemplo e melhorar, se necessário. Consulte o documento [menu de navegação](Navigation-Menu.md) para obter mais informações. + +### Contribuintes de Barra de Ferramentas + +O sistema de [barra de ferramentas](Toolbars.md) é usado para definir **barras de ferramentas** na interface do usuário. Os módulos (ou seu aplicativo) podem adicionar **itens** a uma barra de ferramentas e, em seguida, o tema renderiza a barra de ferramentas no **layout**. + +Existe apenas uma **barra de ferramentas padrão** (chamada "Principal" - definida como uma constante: `StandardToolbars.Main`). Para o tema básico, ele é renderizado como mostrado abaixo:![bookstore-toolbar-highlighted](../../images/bookstore-toolbar-highlighted.png) + +Na captura de tela acima, existem dois itens adicionados à barra de ferramentas principal: componente de troca de idioma e menu do usuário. Você pode adicionar seus próprios itens aqui. + +#### Exemplo: Adicionar um Ícone de Notificação + +Neste exemplo, adicionaremos um **ícone de notificação (sino)** à esquerda do item de troca de idioma. Um item na barra de ferramentas deve ser um **componente de visualização**. Portanto, primeiro, crie um novo componente de visualização em seu projeto: + +![bookstore-notification-view-component](../../images/bookstore-notification-view-component.png) + +**NotificationViewComponent.cs** + +````csharp +public class NotificationViewComponent : AbpViewComponent +{ + public async Task<IViewComponentResult> InvokeAsync() + { + return View("/Pages/Shared/Components/Notification/Default.cshtml"); + } +} +```` + +**Default.cshtml** + +````xml +<div id="MainNotificationIcon" style="color: white; margin: 8px;"> + <i class="far fa-bell"></i> +</div> +```` + +Agora, podemos criar uma classe que implementa a interface `IToolbarContributor`: + +````csharp +public class MyToolbarContributor : IToolbarContributor +{ + public Task ConfigureToolbarAsync(IToolbarConfigurationContext context) + { + if (context.Toolbar.Name == StandardToolbars.Main) + { + context.Toolbar.Items + .Insert(0, new ToolbarItem(typeof(NotificationViewComponent))); + } + + return Task.CompletedTask; + } +} +```` + +Esta classe adiciona o `NotificationViewComponent` como o primeiro item na barra de ferramentas `Main`. + +Finalmente, você precisa adicionar este contribuinte ao `AbpToolbarOptions`, no `ConfigureServices` do seu módulo: + +````csharp +Configure<AbpToolbarOptions>(options => +{ + options.Contributors.Add(new MyToolbarContributor()); +}); +```` + +Isso é tudo, você verá o ícone de notificação na barra de ferramentas quando executar o aplicativo: + +![bookstore-notification-icon-on-toolbar](../../images/bookstore-notification-icon-on-toolbar.png) + +O `NotificationViewComponent` neste exemplo simplesmente retorna uma visualização sem nenhum dado. Na vida real, você provavelmente desejará **consultar o banco de dados** (ou chamar uma API HTTP) para obter notificações e passá-las para a visualização. Se necessário, você pode adicionar um arquivo `JavaScript` ou `CSS` ao pacote global (como descrito anteriormente) para o item da barra de ferramentas. + +Consulte o documento [barras de ferramentas](Toolbars.md) para obter mais informações sobre o sistema de barras de ferramentas. + +### Hooks de Layout + +O sistema de [Hooks de Layout](Layout-Hooks.md) permite que você **adicione código** em algumas partes específicas do layout. Todos os layouts de todos os temas devem implementar esses hooks. Em seguida, você pode adicionar um **componente de visualização** em um ponto de hook. + +#### Exemplo: Adicionar Script do Google Analytics + +Suponha que você precise adicionar o script do Google Analytics ao layout (que estará disponível para todas as páginas). Primeiro, **crie um componente de visualização** em seu projeto: + +![bookstore-google-analytics-view-component](../../images/bookstore-google-analytics-view-component.png) + +**GoogleAnalyticsViewComponent.cs** + +````csharp +public class GoogleAnalyticsViewComponent : AbpViewComponent +{ + public IViewComponentResult Invoke() + { + return View("/Pages/Shared/Components/GoogleAnalytics/Default.cshtml"); + } +} +```` + +**Default.cshtml** + +````html +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-xxxxxx-1', 'auto'); + ga('send', 'pageview'); +</script> +```` + +Altere `UA-xxxxxx-1` para o seu próprio código. + +Você pode então adicionar este componente a qualquer um dos pontos de hook no `ConfigureServices` do seu módulo: + +````csharp +Configure<AbpLayoutHookOptions>(options => +{ + options.Add( + LayoutHooks.Head.Last, //O nome do hook + typeof(GoogleAnalyticsViewComponent) //O componente a ser adicionado + ); +}); +```` + +Agora, o código do GA será inserido no `head` da página como o último item. Você (ou os módulos que você está usando) pode adicionar vários itens ao mesmo hook. Todos eles serão adicionados ao layout. + +A configuração acima adiciona o `GoogleAnalyticsViewComponent` a todos os layouts. Talvez você queira adicionar apenas a um layout específico: + +````csharp +Configure<AbpLayoutHookOptions>(options => +{ + options.Add( + LayoutHooks.Head.Last, + typeof(GoogleAnalyticsViewComponent), + layout: StandardLayouts.Application //Defina o layout a ser adicionado + ); +}); +```` + +Consulte a seção de layouts abaixo para saber mais sobre o sistema de layout. + +### Layouts + +O sistema de layout permite que os temas definam layouts padrão e nomeados e permite que qualquer página selecione um layout adequado para seu propósito. Existem três layouts predefinidos: + +* "**Application**": O layout principal (e o padrão) para um aplicativo. Normalmente, contém cabeçalho, menu (barra lateral), rodapé, barra de ferramentas... etc. +* "**Account**": Este layout é usado para login, registro e outras páginas semelhantes. É usado para as páginas na pasta `/Pages/Account` por padrão. +* "**Empty**": Layout vazio e mínimo. + +Esses nomes são definidos na classe `StandardLayouts` como constantes. Você pode definitivamente criar seus próprios layouts, mas esses são os nomes padrão dos layouts e são implementados por todos os temas por padrão. + +#### Localização do Layout + +Você pode encontrar os arquivos de layout [aqui](https://github.com/abpframework/abp/blob/dev/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts) para o tema básico. Você pode usá-los como referência para criar seus próprios layouts ou pode substituí-los, se necessário. + +#### ITheme + +O ABP Framework usa o serviço `ITheme` para obter a localização do layout pelo nome do layout. Você pode substituir este serviço para selecionar dinamicamente a localização do layout. + +#### IThemeManager + +`IThemeManager` é usado para obter o tema atual e obter o caminho do layout. Qualquer página pode determinar seu próprio layout. Exemplo: + +````html +@using Volo.Abp.AspNetCore.Mvc.UI.Theming +@inject IThemeManager ThemeManager +@{ + Layout = ThemeManager.CurrentTheme.GetLayout(StandardLayouts.Empty); +} +```` + +Esta página usará o layout vazio. Você usa o método de extensão `ThemeManager.CurrentTheme.GetEmptyLayout();` como atalho. + +Se você deseja definir o layout para todas as páginas em uma pasta específica, escreva o código acima em um arquivo `_ViewStart.cshtml` dentro dessa pasta. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Data-Table-Column-Extensions.md b/docs/pt-BR/UI/AspNetCore/Data-Table-Column-Extensions.md new file mode 100644 index 0000000000..43fd7555b9 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Data-Table-Column-Extensions.md @@ -0,0 +1,161 @@ +# Extensões de Colunas de Tabela de Dados para a Interface do Usuário ASP.NET Core + +## Introdução + +O sistema de extensão de colunas de tabela de dados permite adicionar uma **nova coluna de tabela** na interface do usuário. O exemplo abaixo adiciona uma nova coluna com o título "Número de Seguro Social": + +![user-action-extension-click-me](../../images/table-column-extension-example.png) + +Você pode usar as opções de coluna padrão para controlar a coluna da tabela com precisão. + +> Observe que esta é uma API de baixo nível para controlar a coluna da tabela. Se você deseja mostrar uma propriedade de extensão na tabela, consulte o documento [extensão de entidade de módulo](../../Module-Entity-Extensions.md). + +## Como Configurar + +### Criar um Arquivo JavaScript + +Primeiro, adicione um novo arquivo JavaScript à sua solução. Nós adicionamos dentro da pasta `/Pages/Identity/Users` do projeto `.Web`: + +![user-action-extension-on-solution](../../images/user-action-extension-on-solution.png) + +Aqui está o conteúdo deste arquivo JavaScript: + +```js +abp.ui.extensions.tableColumns + .get('identity.user') + .addContributor(function (columnList) { + columnList.addTail({ //adicionar como a última coluna + title: 'Número de Seguro Social', + data: 'extraProperties.SocialSecurityNumber', + orderable: false, + render: function (data, type, row) { + if (row.extraProperties.SocialSecurityNumber) { + return '<strong>' + + row.extraProperties.SocialSecurityNumber + + '<strong>'; + } else { + return '<i class="text-muted">indefinido</i>'; + } + } + }); + }); +``` + +Este exemplo define uma função `render` personalizada para retornar um HTML personalizado para ser renderizado na coluna. + +### Adicionar o Arquivo à Página de Gerenciamento de Usuários + +Em seguida, você precisa adicionar este arquivo JavaScript à página de gerenciamento de usuários. Você pode aproveitar o poder do sistema de [Agrupamento e Minificação](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification). + +Escreva o seguinte código dentro do método `ConfigureServices` da sua classe de módulo: + +```csharp +Configure<AbpBundlingOptions>(options => +{ + options.ScriptBundles.Configure( + typeof(Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel).FullName, + bundleConfiguration => + { + bundleConfiguration.AddFiles( + "/Pages/Identity/Users/my-user-extensions.js" + ); + }); +}); +``` + +Esta configuração adiciona `my-user-extensions.js` à página de gerenciamento de usuários do Módulo de Identidade. `typeof(Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel).FullName` é o nome do pacote na página de gerenciamento de usuários. Esta é uma convenção comum usada para todos os módulos comerciais do ABP. + +### Renderizando a Coluna + +Este exemplo pressupõe que você definiu uma propriedade extra `SocialSecurityNumber` usando o sistema de [extensão de entidade de módulo](../../Module-Entity-Extensions.md). No entanto; + +* Você pode adicionar uma nova coluna relacionada a uma propriedade existente do usuário (que não foi adicionada à tabela por padrão). Exemplo: + +````js +abp.ui.extensions.tableColumns + .get('identity.user') + .addContributor(function (columnList) { + columnList.addTail({ + title: 'Telefone confirmado?', + data: 'phoneNumberConfirmed', + render: function (data, type, row) { + if (row.phoneNumberConfirmed) { + return '<strong style="color: green">SIM<strong>'; + } else { + return '<i class="text-muted">NÃO</i>'; + } + } + }); + }); +```` + +* Você pode adicionar uma nova coluna personalizada que não está relacionada a nenhuma propriedade da entidade, mas sim a uma informação completamente personalizada. Exemplo: + +````js +abp.ui.extensions.tableColumns + .get('identity.user') + .addContributor(function (columnList) { + columnList.addTail({ + title: 'Coluna personalizada', + data: {}, + orderable: false, + render: function (data) { + if (data.phoneNumber) { + return "ligar: " + data.phoneNumber; + } else { + return ''; + } + } + }); + }); +```` + +## API + +Esta seção explica os detalhes da API JavaScript `abp.ui.extensions.tableColumns`. + +### abp.ui.extensions.tableColumns.get(nomeEntidade) + +Este método é usado para acessar as colunas da tabela para uma entidade de um módulo específico. Ele recebe um parâmetro: + +* **nomeEntidade**: O nome da entidade definido pelo módulo relacionado. + +### abp.ui.extensions.tableColumns.get(nomeEntidade).columns + +A propriedade `columns` é usada para recuperar uma [lista duplamente encadeada](../Common/Utils/Linked-List.md) de colunas previamente definidas para uma tabela. Todos os contribuidores são executados na ordem para preparar a lista final de colunas. Isso é normalmente chamado pelos módulos para mostrar as colunas na tabela. No entanto, você pode usá-lo se estiver construindo suas próprias interfaces extensíveis. + +### abp.ui.extensions.tableColumns.get(nomeEntidade).addContributor(contributeCallback [, order]) + +O método `addContributor` cobre todos os cenários, por exemplo, se você deseja adicionar sua coluna em uma posição diferente na lista, alterar ou remover uma coluna existente. `addContributor` tem os seguintes parâmetros: + +* **contributeCallback**: Uma função de retorno de chamada que é chamada sempre que a lista de colunas deve ser criada. Você pode modificar livremente a lista de colunas dentro deste método de retorno de chamada. +* **order** (opcional): A ordem da chamada na lista de chamadas. Sua chamada é adicionada ao final da lista (portanto, você tem a oportunidade de modificar as colunas adicionadas pelos contribuidores anteriores). Você pode definir `0` para adicionar seu contribuidor como o primeiro item. + +#### Exemplo + +```js +var myColumnDefinition = { + title: 'Coluna personalizada', + data: {}, + orderable: false, + render: function(data) { + if (data.phoneNumber) { + return "ligar: " + data.phoneNumber; + } else { + return ''; + } + } +}; + +abp.ui.extensions.tableColumns + .get('identity.user') + .addContributor(function (columnList) { + // Remover um item da lista de ações + columnList.dropHead(); + + // Adicionar um novo item à lista de ações + columnList.addHead(myColumnDefinition); + }); +``` + +> `columnList` é uma [lista encadeada](../Common/Utils/Linked-List.md). Você pode usar seus métodos para construir uma lista de colunas da maneira que precisar. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Data-Tables.md b/docs/pt-BR/UI/AspNetCore/Data-Tables.md new file mode 100644 index 0000000000..5eb1337888 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Data-Tables.md @@ -0,0 +1,298 @@ +# ASP.NET Core MVC / Razor Pages: Tabelas de Dados + +Uma Tabela de Dados (também conhecida como Data Grid) é um componente de interface do usuário para mostrar dados tabulares aos usuários. Existem muitos componentes/bibliotecas de Tabela de Dados e **você pode usar qualquer um que preferir** com o ABP Framework. No entanto, os modelos de inicialização vêm com a biblioteca [DataTables.Net](https://datatables.net/) já **pré-instalada e configurada**. O ABP Framework fornece adaptadores para essa biblioteca e facilita o uso com os endpoints da API. + +Uma captura de tela de exemplo da página de gerenciamento de usuários que mostra a lista de usuários em uma tabela de dados: + +![exemplo-datatables](../../images/exemplo-datatables.png) + +## Integração com o DataTables.Net + +Antes de tudo, você pode seguir a documentação oficial para entender como o [DataTables.Net](https://datatables.net/) funciona. Esta seção se concentrará nos complementos e pontos de integração do ABP, em vez de cobrir completamente o uso dessa biblioteca. + +### Um Exemplo Rápido + +Você pode seguir o [tutorial de desenvolvimento de aplicativos da web](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC) para um exemplo completo de aplicativo que usa o DataTables.Net como Tabela de Dados. Esta seção mostra um exemplo minimalista. + +Você não precisa fazer nada para adicionar a biblioteca DataTables.Net à página, pois ela já está adicionada ao [pacote](Bundling-Minification.md) global por padrão. + +Primeiro, adicione um `abp-table` como mostrado abaixo, com um `id`: + +````html +<abp-table striped-rows="true" id="BooksTable"></abp-table> +```` + +> `abp-table` é um [Tag Helper](Tag-Helpers/Index.md) definido pelo ABP Framework, mas uma simples tag `<table...>` também funcionaria. + +Em seguida, chame o plugin `DataTable` no seletor da tabela: + +````js +var dataTable = $('#BooksTable').DataTable( + abp.libs.datatables.normalizeConfiguration({ + serverSide: true, + paging: true, + order: [[1, "asc"]], + searching: false, + ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList), + columnDefs: [ + { + title: l('Ações'), + rowAction: { + items: + [ + { + text: l('Editar'), + action: function (data) { + ///... + } + } + ] + } + }, + { + title: l('Nome'), + data: "name" + }, + { + title: l('Data de Publicação'), + data: "publishDate", + render: function (data) { + return luxon + .DateTime + .fromISO(data, { + locale: abp.localization.currentCulture.name + }).toLocaleString(); + } + }, + { + title: l('Preço'), + data: "price" + } + ] + }) +); +```` + +O código de exemplo acima usa algumas funcionalidades de integração do ABP que serão explicadas nas próximas seções. + +### Normalização de Configuração + +A função `abp.libs.datatables.normalizeConfiguration` recebe uma configuração do DataTables e a normaliza para simplificá-la; + +* Define a opção `scrollX` como `true`, se não estiver definida. +* Define o índice `target` para as definições de coluna. +* Define a opção `language` para [localizar](../../Localization.md) a tabela no idioma atual. + +#### Configuração Padrão + +`normalizeConfiguration` usa a configuração padrão. Você pode alterar a configuração padrão usando o objeto `abp.libs.datatables.defaultConfigurations`. Exemplo: + +````js +abp.libs.datatables.defaultConfigurations.scrollX = false; +```` + +Aqui estão todas as opções de configuração; + +* `scrollX`: `false` por padrão. +* `dom`: O valor padrão é `<"dataTable_filters"f>rt<"row dataTable_footer"<"col-auto"l><"col-auto"i><"col"p>>`. +* `language`: Uma função que retorna o texto de localização usando o idioma atual. + +### Adaptador AJAX + +O DataTables.Net possui seu próprio formato de dados esperado ao obter os resultados de uma chamada AJAX para o servidor para obter os dados da tabela. Eles estão especialmente relacionados a como os parâmetros de paginação e ordenação são enviados e recebidos. O ABP Framework também oferece suas próprias convenções para a comunicação [AJAX](JavaScript-API/Ajax.md) cliente-servidor. + +O método `abp.libs.datatables.createAjax` (usado no exemplo acima) adapta o formato dos dados de solicitação e resposta e funciona perfeitamente com o sistema [Dynamic JavaScript Client Proxy](Dynamic-JavaScript-Proxies.md). + +Isso funciona automaticamente, então na maioria das vezes você não precisa saber como funciona. Consulte o documento [DTO](../../Data-Transfer-Objects.md) se você quiser aprender mais sobre `IPagedAndSortedResultRequest`, `IPagedResult` e outras interfaces padrão e classes DTO base usadas na comunicação cliente-servidor. + +O `createAjax` também permite que você personalize os parâmetros de solicitação e manipule as respostas. + +**Exemplo:** + +````csharp +var inputAction = function (requestData, dataTableSettings) { + return { + id: $('#Id').val(), + name: $('#Name').val(), + }; +}; + +var responseCallback = function(result) { + + // seu código personalizado. + + return { + recordsTotal: result.totalCount, + recordsFiltered: result.totalCount, + data: result.items + }; +}; + +ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList, inputAction, responseCallback) +```` + +Se você não precisa acessar ou modificar o `requestData` ou o `dataTableSettings`, você pode especificar um objeto simples como segundo parâmetro. + +````js +ajax: abp.libs.datatables.createAjax( + acme.bookStore.books.book.getList, + { id: $('#Id').val(), name: $('#Name').val() } +) +```` + +### Ações de Linha + +`rowAction` é uma opção definida pelo ABP Framework para as definições de coluna para mostrar um botão suspenso para executar ações para uma linha na tabela. + +A captura de tela de exemplo abaixo mostra as ações para cada usuário na tabela de gerenciamento de usuários: + +![exemplo-datatables](../../images/exemplo-datatables-acao-linha.png) + +`rowAction` é definido como parte de uma definição de coluna: + +````csharp +{ + title: l('Ações'), + rowAction: { + //TODO: CONFIGURAÇÃO + } +}, +```` + +**Exemplo: Mostrar ações *Editar* e *Excluir* para uma linha de livro** + +````js +{ + title: l('Ações'), + rowAction: { + items: + [ + { + text: l('Editar'), + action: function (data) { + //TODO: Abrir um modal para editar o livro + } + }, + { + text: l('Excluir'), + confirmMessage: function (data) { + return "Tem certeza de que deseja excluir o livro " + data.record.name; + }, + action: function (data) { + acme.bookStore.books.book + .delete(data.record.id) + .then(function() { + abp.notify.info("Excluído com sucesso!"); + data.table.ajax.reload(); + }); + } + } + ] + } +}, +```` + +#### Itens de Ação + +`items` é uma matriz de definições de ação. Uma definição de ação pode ter as seguintes opções; + +* `text`: O texto (uma `string`) para esta ação a ser mostrada no menu suspenso de ações. +* `action`: Uma `function` que é executada quando o usuário clica na ação. A função recebe um argumento `data` que possui os seguintes campos; + * `data.record`: Este é o objeto de dados relacionado à linha. Você pode acessar os campos de dados como `data.record.id`, `data.record.name`... etc. + * `data.table`: A instância DataTables. +* `confirmMessage`: Uma `function` (veja o exemplo acima) que retorna uma mensagem (`string`) para mostrar um diálogo para obter uma confirmação do usuário antes de executar a `action`. Exemplo de diálogo de confirmação: + +![exemplo-datatables-acao-linha-confirmacao](../../images/exemplo-datatables-acao-linha-confirmacao.png) + +Você pode usar o sistema de [localização](JavaScript-API/Localization.md) para mostrar uma mensagem localizada. + +* `visible`: Um `bool` ou uma `function` que retorna um `bool`. Se o resultado for `false`, então a ação não é mostrada no menu suspenso de ações. Isso geralmente é combinado com o sistema de [autorização](JavaScript-API/Auth.md) para ocultar a ação se o usuário não tiver permissão para executar essa ação. Exemplo: + +````js +visible: abp.auth.isGranted('BookStore.Books.Delete'); +```` + +Se você definir uma `function`, então a `function` tem dois argumentos: `record` (o objeto de dados da linha relacionada) e a `table` (a instância DataTables). Portanto, você pode decidir mostrar/ocultar a ação dinamicamente, com base nos dados da linha e em outras condições. + +* `iconClass`: Pode ser usado para mostrar um ícone de fonte, como um ícone [Font-Awesome](https://fontawesome.com/) (ex: `fas fa-trash-alt`), próximo ao texto da ação. Captura de tela de exemplo: + +![exemplo-datatables-acao-linha-icon](../../images/exemplo-datatables-acao-linha-icon.png) + +* `enabled`: Uma `function` que retorna um `bool` para desabilitar a ação. A `function` recebe um objeto `data` com dois campos: `data.record` é o objeto de dados relacionado à linha e `data.table` é a instância DataTables. +* `displayNameHtml`: Defina isso como `true` se o valor `text` contiver tags HTML. + +Existem algumas regras com os itens de ação; + +* Se nenhum dos itens de ação for visível, a coluna de ações não será renderizada. + +### Formato de Dados + +#### O Problema + +Veja a coluna *Data de Criação* no exemplo abaixo: + +````js +{ + title: l('CreationTime'), + data: "creationTime", + render: function (data) { + return luxon + .DateTime + .fromISO(data, { + locale: abp.localization.currentCulture.name + }).toLocaleString(luxon.DateTime.DATETIME_SHORT); + } +} +```` + +O `render` é uma opção padrão do DataTables para renderizar o conteúdo da coluna por uma função personalizada. Este exemplo usa a biblioteca [luxon](https://moment.github.io/luxon/) (que é instalada por padrão) para escrever um valor legível por humanos do `creationTime` no idioma atual do usuário. Exemplo de saída da coluna: + +![exemplo-datatables-custom-render-date](../../images/exemplo-datatables-custom-render-date.png) + +Se você não definir a opção de renderização, o resultado será feio e não amigável ao usuário: + +![exemplo-datatables-custom-render-date](../../images/exemplo-datatables-default-render-date.png) + +No entanto, renderizar um `DateTime` é quase o mesmo e repetir a mesma lógica de renderização em todos os lugares vai contra o princípio DRY (Don't Repeat Yourself!). + +#### Opção dataFormat + +A opção `dataFormat` da coluna especifica o formato de dados que é usado para renderizar os dados da coluna. A mesma saída poderia ser alcançada usando a seguinte definição de coluna: + +````js +{ + title: l('CreationTime'), + data: "creationTime", + dataFormat: 'datetime' +} +```` + +`dataFormat: 'datetime'` especifica o formato de dados para esta coluna. Existem alguns `dataFormat`s pré-definidos: + +* `boolean`: Mostra um ícone de `check` para o valor `true` e um ícone de `times` para o valor `false` e é útil para renderizar valores `bool`. +* `date`: Mostra a parte da data de um valor `DateTime`, formatado com base na cultura atual. +* `datetime`: Mostra a data e a hora (excluindo segundos) de um valor `DateTime`, formatado com base na cultura atual. + +### Renderizadores Padrão + +A opção `abp.libs.datatables.defaultRenderers` permite que você defina novos formatos de dados e defina renderizadores para eles. + +**Exemplo: Renderizar ícones de masculino/feminino com base no gênero** + +````js +abp.libs.datatables.defaultRenderers['gender'] = function(value) { + if (value === 'f') { + return '<i class="fa fa-venus"></i>'; + } else { + return '<i class="fa fa-mars"></i>'; + } +}; +```` + +Supondo que os valores possíveis para os dados de uma coluna sejam `f` e `m`, o formato de dados `gender` mostra ícones femininos/masculinos em vez dos textos `f` e `m`. Agora você pode definir `dataFormat: 'gender'` para uma definição de coluna que tenha os valores de dados adequados. + +> Você pode escrever os renderizadores padrão em um único arquivo JavaScript e adicioná-lo ao [Pacote de Scripts Global](Bundling-Minification.md), para que você possa reutilizá-los em todas as páginas. + +## Outras Tabelas de Dados + +Você pode usar qualquer biblioteca que preferir. Por exemplo, [veja este artigo](https://community.abp.io/articles/using-devextreme-components-with-the-abp-framework-zb8z7yqv) para aprender como usar o DevExtreme Data Grid em seus aplicativos. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Dynamic-JavaScript-Proxies.md b/docs/pt-BR/UI/AspNetCore/Dynamic-JavaScript-Proxies.md new file mode 100644 index 0000000000..3584c9e22c --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Dynamic-JavaScript-Proxies.md @@ -0,0 +1,97 @@ +# Proxies de Cliente de API JavaScript Dinâmico + +É comum consumir suas APIs HTTP a partir do seu código JavaScript. Para fazer isso, normalmente você lida com chamadas AJAX de baixo nível, como $.ajax, ou melhor [abp.ajax](JavaScript-API/Ajax.md). O ABP Framework fornece **uma maneira melhor** de chamar suas APIs HTTP a partir do seu código JavaScript: Proxies de Cliente de API JavaScript! + +## Proxies de Cliente JavaScript Estáticos vs Dinâmicos + +O ABP fornece **dois tipos** de sistema de geração de proxy de cliente. Este documento explica os **proxies de cliente dinâmicos**, que geram proxies do lado do cliente em tempo de execução. Você também pode ver a documentação de [Proxies de Cliente de API JavaScript Estáticos](Static-JavaScript-Proxies.md) para aprender como gerar proxies em tempo de desenvolvimento. + +A geração de proxy de cliente em tempo de desenvolvimento (estático) tem uma **ligeira vantagem de desempenho**, pois não precisa obter a definição da API HTTP em tempo de execução. No entanto, você deve **regenerar** o código do proxy de cliente sempre que alterar a definição do ponto de extremidade da API. Por outro lado, os proxies de cliente dinâmicos são gerados em tempo de execução e oferecem uma **experiência de desenvolvimento mais fácil**. + +## Um Exemplo Rápido + +Suponha que você tenha um serviço de aplicativo definido como mostrado abaixo: + +````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<AuthorDto> GetAsync(Guid id); + + Task<PagedResultDto<AuthorDto>> GetListAsync(GetAuthorListDto input); + + Task<AuthorDto> CreateAsync(CreateAuthorDto input); + + Task UpdateAsync(Guid id, UpdateAuthorDto input); + + Task DeleteAsync(Guid id); + } +} +```` + +> Você pode seguir o [tutorial de desenvolvimento de aplicativos da web](../../Tutorials/Part-1.md) para aprender como criar [serviços de aplicativos](../../Application-Services.md), expô-los como [APIs HTTP](../../API/Auto-API-Controllers.md) e consumir do código JavaScript como um exemplo completo. + +Você pode chamar qualquer um dos métodos como se estivesse chamando uma função JavaScript. A função JavaScript tem o mesmo **nome**, **parâmetros** e **valor de retorno** do método C#. + +**Exemplo: Obter a lista de autores** + +````js +acme.bookStore.authors.author.getList({ + maxResultCount: 10 +}).then(function(result){ + console.log(result.items); +}); +```` + +**Exemplo: Excluir um autor** + +```js +acme.bookStore.authors.author + .delete('7245a066-5457-4941-8aa7-3004778775f0') //Obtenha o id de algum lugar! + .then(function() { + abp.notify.info('Excluído com sucesso!'); + }); +``` + +## Detalhes do AJAX + +As funções de proxy de cliente JavaScript usam o [abp.ajax](JavaScript-API/Ajax.md) por baixo dos panos. Portanto, você tem os mesmos benefícios, como **tratamento automático de erros**. Além disso, você pode controlar totalmente a chamada AJAX fornecendo as opções. + +### O Valor de Retorno + +Cada função retorna um [objeto Deferred](https://api.jquery.com/category/deferred-object/). Isso significa que você pode encadear com `then` para obter o resultado, `catch` para lidar com o erro, `always` para executar uma ação assim que a operação for concluída (com sucesso ou falha). + +### Opções do AJAX + +Cada função recebe um **último parâmetro** adicional após seus próprios parâmetros. O último parâmetro é chamado de `ajaxParams`. É um objeto que substitui as opções do AJAX. + +**Exemplo: Definir as opções do AJAX `type` e `dataType`** + +````js +acme.bookStore.authors.author + .delete('7245a066-5457-4941-8aa7-3004778775f0', { + type: 'POST', + dataType: 'xml' + }) + .then(function() { + abp.notify.info('Excluído com sucesso!'); + }); +```` + +Consulte a documentação do [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) para todas as opções disponíveis. + +## Endpoint do Script de Proxy de Serviço + +A mágica é feita pelo endpoint `/Abp/ServiceProxyScript` definido pelo ABP Framework e adicionado automaticamente ao layout. Você pode visitar este endpoint em sua aplicação para ver as definições das funções de proxy de cliente. Este arquivo de script é gerado automaticamente pelo ABP Framework com base nas definições dos métodos do lado do servidor e nos detalhes do ponto de extremidade HTTP relacionado. + +## Veja Também + +* [Proxies de Cliente de API JavaScript Estáticos](Static-JavaScript-Proxies.md) +* [Controladores de API Automáticos](../../API/Auto-API-Controllers.md) +* [Tutorial de Desenvolvimento de Aplicativos da Web](../../Tutorials/Part-1.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Entity-Action-Extensions.md b/docs/pt-BR/UI/AspNetCore/Entity-Action-Extensions.md new file mode 100644 index 0000000000..396d2de7b2 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Entity-Action-Extensions.md @@ -0,0 +1,108 @@ +# Extensões de Ação de Entidade para a Interface do Usuário do ASP.NET Core + +## Introdução + +O sistema de extensão de ação de entidade permite adicionar uma **nova ação** ao menu de ações de uma entidade. Uma ação **Clique em Mim** foi adicionada à página de *Gerenciamento de Usuários* abaixo: + +![user-action-extension-click-me](../../images/user-action-extension-click-me.png) + +Você pode executar qualquer ação (abrir um modal, fazer uma chamada de API HTTP, redirecionar para outra página... etc) escrevendo seu código personalizado. Você pode acessar a entidade atual em seu código. + +## Como Configurar + +Neste exemplo, adicionaremos uma ação "Clique em Mim!" e executaremos um código JavaScript para a página de gerenciamento de usuários do [Módulo de Identidade](../../Modules/Identity.md). + +### Criar um Arquivo JavaScript + +Primeiro, adicione um novo arquivo JavaScript à sua solução. Nós adicionamos dentro da pasta `/Pages/Identity/Users` do projeto `.Web`: + +![user-action-extension-on-solution](../../images/user-action-extension-on-solution.png) + +Aqui está o conteúdo deste arquivo JavaScript: + +```js +var clickMeAction = { + text: 'Clique em Mim!', + action: function(data) { + //TODO: Escreva seu código personalizado + alert(data.record.userName); + } +}; + +abp.ui.extensions.entityActions + .get('identity.user') + .addContributor(function(actionList) { + actionList.addTail(clickMeAction); + }); +``` + +Na função `action`, você pode fazer qualquer coisa que precisar. Consulte a seção API para obter um uso detalhado. + +### Adicionar o Arquivo à Página de Gerenciamento de Usuários + +Em seguida, você precisa adicionar este arquivo JavaScript à página de gerenciamento de usuários. Você pode aproveitar o poder do [Sistema de Agrupamento e Minificação](Bundling-Minification.md). + +Escreva o seguinte código dentro do método `ConfigureServices` da sua classe de módulo: + +```csharp +Configure<AbpBundlingOptions>(options => +{ + options.ScriptBundles.Configure( + typeof(Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel).FullName, + bundleConfiguration => + { + bundleConfiguration.AddFiles( + "/Pages/Identity/Users/my-user-extensions.js" + ); + }); +}); +``` + +Essa configuração adiciona `my-user-extensions.js` à página de gerenciamento de usuários do Módulo de Identidade. `typeof(Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel).FullName` é o nome do pacote na página de gerenciamento de usuários. Essa é uma convenção comum usada para todos os módulos comerciais do ABP. + +Isso é tudo. Execute sua aplicação para ver o resultado. + +## API + +Esta seção explica os detalhes da API JavaScript `abp.ui.extensions.entityActions`. + +### abp.ui.extensions.entityActions.get(entityName) + +Este método é usado para acessar as ações de entidade de um módulo específico. Ele recebe um parâmetro: + +* **entityName**: O nome da entidade definido pelo módulo relacionado. + +### abp.ui.extensions.entityActions.get(entityName).actions + +A propriedade `actions` é usada para recuperar uma [lista duplamente encadeada](../Common/Utils/Linked-List.md) de ações previamente definidas para uma entidade. Todos os contribuidores são executados para preparar a lista final de ações. Isso é normalmente chamado pelos módulos para mostrar as ações na grade. No entanto, você pode usá-lo se estiver construindo suas próprias interfaces extensíveis. + +### abp.ui.extensions.entityActions.get(entityName).addContributor(contributeCallback) + +O método `addContributor` cobre todos os cenários, por exemplo, se você deseja adicionar sua ação em uma posição diferente na lista, alterar ou remover um item de ação existente. `addContributor` com o seguinte parâmetro: + +* **contributeCallback**: Uma função de retorno de chamada que é chamada sempre que a lista de ações deve ser criada. Você pode modificar livremente a lista de ações dentro deste método de retorno de chamada. + +#### Exemplo + +```js +var clickMe2Action = { + text: 'Clique em Mim 2!', + icon: 'fas fa-hand-point-right', + action: function(data) { + //TODO: Escreva seu código personalizado + alert(data.record.userName); + } +}; + +abp.ui.extensions.entityActions + .get('identity.user') + .addContributor(function(actionList) { + // Remover um item da actionList + actionList.dropHead(); + + // Adicionar o novo item à actionList + actionList.addHead(clickMe2Action); + }); +``` + +> `actionList` é uma [lista encadeada](../Common/Utils/Linked-List.md). Você pode usar seus métodos para construir uma lista de colunas da maneira que precisar. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Forms-Validation.md b/docs/pt-BR/UI/AspNetCore/Forms-Validation.md new file mode 100644 index 0000000000..29eb88aee6 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Forms-Validation.md @@ -0,0 +1,227 @@ +# ASP.NET Core MVC / Razor Pages: Formulários e Validação + +O ABP Framework fornece infraestrutura e convenções para facilitar a criação de formulários, localizar nomes de exibição para os elementos do formulário e lidar com validação no lado do servidor e do cliente; + +* O helper de tag [abp-dynamic-form](Tag-Helpers/Dynamic-Forms.md) automatiza a criação de um **formulário completo** a partir de uma classe de modelo C#: cria os elementos de entrada, lida com a localização e validação no lado do cliente. +* Os helpers de tag [ABP Form](Tag-Helpers/Form-elements.md) (`abp-input`, `abp-select`, `abp-radio`...) renderizam **um único elemento de formulário** com localização e validação no lado do cliente. +* O ABP Framework automaticamente **localiza o nome de exibição** de um elemento do formulário sem a necessidade de adicionar um atributo `[DisplayName]`. +* Os **erros de validação** são automaticamente localizados com base na cultura do usuário. + +> Este documento é para a **validação no lado do cliente** e não cobre a validação no lado do servidor. Verifique o [documento de validação](../../Validation.md) para a infraestrutura de validação no lado do servidor. + +## O Modo Clássico + +Em uma típica interface de usuário ASP.NET Core MVC / Razor Pages baseada no Bootstrap, você [precisa escrever](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation#client-side-validation) um código boilerplate como este para criar um elemento de formulário simples: + +````html +<div class="form-group"> + <label asp-for="Movie.ReleaseDate" class="control-label"></label> + <input asp-for="Movie.ReleaseDate" class="form-control" /> + <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span> +</div> +```` + +Você pode continuar usando essa abordagem se precisar ou preferir. No entanto, os helpers de tag do ABP Form podem produzir a mesma saída com um código mínimo. + +## ABP Dynamic Forms + +O helper de tag [abp-dynamic-form](Tag-Helpers/Dynamic-Forms.md) automatiza completamente a criação do formulário. Considere esta classe de modelo como exemplo: + +```csharp +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace MyProject.Web.Pages +{ + public class MovieViewModel + { + [Required] + [StringLength(256)] + public string Name { get; set; } + + [Required] + [DataType(DataType.Date)] + public DateTime ReleaseDate { get; set; } + + [Required] + [TextArea] + [StringLength(1000)] + public string Description { get; set; } + + public Genre Genre { get; set; } + + public float? Price { get; set; } + + public bool PreOrder { get; set; } + } +} +``` + +Ele usa os atributos de anotação de dados para definir regras de validação e estilos de UI para as propriedades. `Genre` é um `enum` neste exemplo: + +````csharp +namespace MyProject.Web.Pages +{ + public enum Genre + { + Classic, + Action, + Fiction, + Fantasy, + Animation + } +} +```` + +Para criar o formulário em uma página razor, crie uma propriedade na sua classe `PageModel`: + +```csharp +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace MyProject.Web.Pages +{ + public class CreateMovieModel : PageModel + { + [BindProperty] + public MovieViewModel Movie { get; set; } + + public void OnGet() + { + Movie = new MovieViewModel(); + } + + public async Task OnPostAsync() + { + if (ModelState.IsValid) + { + //TODO: Salvar o Filme + } + } + } +} +``` + +Em seguida, você pode renderizar o formulário no arquivo `.cshtml`: + +```html +@page +@model MyProject.Web.Pages.CreateMovieModel + +<h2>Criar um novo Filme</h2> + +<abp-dynamic-form abp-model="Movie" submit-button="true" /> +``` + +O resultado é mostrado abaixo: + +![abp-dynamic-form-result](../../images/abp-dynamic-form-result.png) + +Consulte a seção *Localização e Validação* abaixo para localizar os nomes de exibição dos campos e ver como a validação funciona. + +> Consulte [seu próprio documento](Tag-Helpers/Dynamic-Forms.md) para todas as opções do helper de tag `abp-dynamic-form`. + +## ABP Form Tag Helpers + +O `abp-dynamic-form` cobre a maioria dos cenários e permite controlar e personalizar o formulário usando os atributos. + +No entanto, se você quiser **renderizar o corpo do formulário você mesmo** (por exemplo, se você quiser ter controle total sobre o **layout do formulário**), você pode usar diretamente os [ABP Form Tag Helpers](Tag-Helpers/Form-elements.md). O mesmo formulário auto-gerado acima pode ser criado usando os ABP Form Tag Helpers, como mostrado abaixo: + +```html +@page +@model MyProject.Web.Pages.CreateMovieModel + +<h2>Criar um novo Filme</h2> + +<form method="post"> + <abp-input asp-for="Movie.Name"/> + <abp-input asp-for="Movie.ReleaseDate"/> + <abp-input asp-for="Movie.Description"/> + <abp-select asp-for="Movie.Genre"/> + <abp-input asp-for="Movie.Price"/> + <abp-input asp-for="Movie.PreOrder"/> + <abp-button button-type="Primary" type="submit">Salvar</abp-button> +</form> +``` + +> Consulte o documento [ABP Form Tag Helpers](Tag-Helpers/Form-elements.md) para obter detalhes desses helpers de tag e suas opções. + +## Validação e Localização + +Tanto o Dynamic Form quanto os Form Tag Helpers **validam automaticamente** a entrada com base nos atributos de anotação de dados e exibem mensagens de erro de validação na interface do usuário. As mensagens de erro são **automaticamente localizadas** com base na cultura atual. + +**Exemplo: O usuário deixa vazio uma propriedade de string obrigatória** + +![abp-form-input-validation-error](../../images/abp-form-input-validation-error.png) + +A mensagem de erro abaixo é mostrada se o idioma for francês: + +![abp-form-input-validation-error](../../images/abp-form-input-validation-error-french.png) + +Os erros de validação já estão [traduzidos](https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization) em muitos idiomas. Você pode [contribuir](../../Contribution/Index.md) para a tradução do seu próprio idioma ou substituir os textos para sua própria aplicação seguindo a documentação de [localização](../../Localization.md). + +## Localização do Nome de Exibição + +O ABP Framework usa o nome da propriedade como o nome do campo na interface do usuário. Normalmente, você deseja [localizar](../../Localization.md) esse nome com base na cultura atual. + +O ABP Framework pode localizar convencionalmente os campos na interface do usuário quando você adiciona as chaves de localização aos arquivos JSON de localização. + +Exemplo: Localização em francês para a propriedade *Name* (adicionar ao `fr.json` na aplicação): + +````js +"Name": "Nom" +```` + +Então, a interface do usuário usará o nome fornecido para o idioma francês: + +![abp-form-input-validation-error](../../images/abp-form-input-validation-error-french-name.png) + +### Usando o Prefixo `DisplayName:` + +Usar diretamente o nome da propriedade como chave de localização pode ser um problema se você precisar usar o nome da propriedade para outro propósito, com um valor de tradução diferente. Nesse caso, use o prefixo `DisplayName:` para a chave de localização: + +````js +"DisplayName:Name": "Nom" +```` + +O ABP prefere usar a chave `DisplayName:Name` em vez da chave `Name` se ela existir. + +### Usando uma Chave de Localização Personalizada + +Se necessário, você pode usar o atributo `[DisplayName]` para especificar a chave de localização para uma propriedade específica: + +````csharp +[DisplayName("MyNameKey")] +public string Name { get; set; } +```` + +Nesse caso, você pode adicionar uma entrada ao arquivo de localização usando a chave `MyNameKey`. + +> Se você usar o `[DisplayName]` mas não adicionar uma entrada correspondente ao arquivo de localização, o ABP Framework mostrará a chave fornecida como o nome do campo, `MyNameKey` para esse caso. Portanto, ele fornece uma maneira de especificar um nome de exibição codificado mesmo se você não precisar usar o sistema de localização. + +### Localização de Enum + +Os membros do Enum também são automaticamente localizados sempre que possível. Por exemplo, quando adicionamos `<abp-select asp-for="Movie.Genre"/>` ao formulário (como fizemos na seção *ABP Form Tag Helpers*), o ABP pode preencher automaticamente os nomes localizados dos membros do Enum. Para habilitar isso, você deve definir os valores localizados no arquivo JSON de localização. Exemplo de entradas para o Enum `Genre` definido na seção *ABP Form Tag Helpers*: + +````json +"Enum:Genre.0": "Filme Clássico", +"Enum:Genre.1": "Filme de Ação", +"Enum:Genre.2": "Ficção", +"Enum:Genre.3": "Fantasia", +"Enum:Genre.4": "Animação/Desenho" +```` + +Você pode usar uma das seguintes sintaxes para as chaves de localização: + +* `Enum:<nome-do-tipo-enum>.<valor-do-enum>` +* `<nome-do-tipo-enum>.<valor-do-enum>` + +> Lembre-se de que se você não especificar valores para o seu Enum, os valores serão ordenados, começando em `0`. + +> Os helpers de tag do MVC também suportam o uso de nomes de membros do Enum em vez de valores (então, você pode definir `"Enum:Genre.Action"` em vez de `"Enum:Genre.1"`, por exemplo), mas isso não é sugerido. Porque, quando você serializa propriedades Enum para JSON e envia para clientes, o serializador padrão usa os valores Enum em vez dos nomes do Enum. Portanto, o nome do Enum não estará disponível para os clientes, e isso será um problema se você quiser usar os mesmos valores de localização no lado do cliente. + +## Veja também + +* [Validação no Lado do Servidor](../../Validation.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Ajax.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Ajax.md new file mode 100644 index 0000000000..3bdae55b3c --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Ajax.md @@ -0,0 +1,150 @@ +# ASP.NET Core MVC / Páginas Razor UI JavaScript AJAX API + +A API `abp.ajax` fornece uma maneira conveniente de realizar chamadas AJAX para o servidor. Ela usa internamente o `$.ajax` do JQuery, mas automatiza algumas tarefas comuns para você: + +* Lida automaticamente com os erros e localiza-os, informando o usuário (usando o [abp.message](Message.md)). Então, normalmente você não precisa se preocupar com os erros. +* Adiciona automaticamente um token de **anti-falsificação** ao cabeçalho HTTP para satisfazer a validação de proteção CSRF no lado do servidor. +* Define automaticamente as opções padrão e permite configurar as opções padrão em um único local. +* Pode **bloquear** uma parte da interface do usuário (ou a página inteira) durante a operação AJAX. +* Permite personalizar completamente qualquer chamada AJAX, usando as opções padrão do `$.ajax`. + +> Embora o `abp.ajax` torne a chamada AJAX mais fácil, normalmente você usará o sistema [Dynamic JavaScript Client Proxy](../Dynamic-JavaScript-Proxies.md) para fazer chamadas às suas APIs HTTP do lado do servidor. O `abp.ajax` pode ser usado quando você precisa realizar operações AJAX de baixo nível. + +## Uso básico + +O `abp.ajax` aceita um objeto de opções que é aceito pelo [$.ajax](https://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) padrão. Todas as opções padrão são válidas. Ele retorna uma [promessa](https://api.jquery.com/category/deferred-object/) como valor de retorno. + +**Exemplo: Obter a lista de usuários** + +````js +abp.ajax({ + type: 'GET', + url: '/api/identity/users' +}).then(function(result){ + console.log(result); +}); +```` + +Este comando registra a lista de usuários no console, se você estiver **logado** na aplicação e tiver [permissão](../../../Authorization.md) para a página de gerenciamento de usuários do [Módulo de Identidade](../../../Modules/Identity.md). + +## Tratamento de erros + +A chamada AJAX de exemplo acima mostra uma **mensagem de erro** se você não estiver logado na aplicação ou não tiver as permissões necessárias para realizar essa solicitação: + +![ajax-error](../../../images/ajax-error.png) + +Todos os tipos de erros são tratados automaticamente pelo `abp.ajax`, a menos que você queira desabilitá-lo. + +### Resposta de erro padrão + +O `abp.ajax` é compatível com o sistema de tratamento de exceções do [ABP Framework](../../../Exception-Handling.md) e lida corretamente com o formato de erro padrão retornado pelo servidor. Uma mensagem de erro típica é um JSON como o abaixo: + +````json +{ + "error": { + "code": "App:010042", + "message": "Este tópico está bloqueado e não é possível adicionar uma nova mensagem", + "details": "Informações mais detalhadas sobre o erro..." + } +} +```` + +A mensagem de erro é mostrada diretamente ao usuário, usando as propriedades `message` e `details`. + +### Resposta de erro não padrão e códigos de status HTTP + +Ele também lida com erros mesmo se o formato de erro padrão não for enviado pelo servidor. Isso pode acontecer se você ignorar o sistema de tratamento de exceções do ABP e construir manualmente a resposta HTTP no servidor. Nesse caso, os **códigos de status HTTP** são considerados. + +Os seguintes códigos de status HTTP são predefinidos: + +* **401**: Mostra uma mensagem de erro como "*Você deve estar autenticado (fazer login) para realizar esta operação*". Quando os usuários clicam no botão OK, eles são redirecionados para a página inicial da aplicação para fazer login novamente. +* **403**: Mostra uma mensagem de erro como "*Você não tem permissão para realizar esta operação*". +* **404**: Mostra uma mensagem de erro como "*O recurso solicitado não foi encontrado no servidor*". +* **Outros**: Mostra uma mensagem de erro genérica como "*Ocorreu um erro. Detalhes do erro não enviados pelo servidor*". + +Todas essas mensagens são localizadas com base no idioma atual do usuário. + +### Tratando manualmente os erros + +Como o `abp.ajax` retorna uma promessa, você sempre pode encadear uma chamada `.catch(...)` para registrar um retorno de chamada que é executado se a solicitação AJAX falhar. + +**Exemplo: Mostrar um alerta se a solicitação AJAX falhar** + +````js +abp.ajax({ + type: 'GET', + url: '/api/identity/users' +}).then(function(result){ + console.log(result); +}).catch(function(){ + alert("solicitação falhou :("); +}); +```` + +Enquanto seu retorno de chamada é executado, o ABP ainda trata o erro por si só. Se você quiser desabilitar o tratamento automático de erros, passe `abpHandleError: false` nas opções do `abp.ajax`. + +**Exemplo: Desabilitar o tratamento automático de erros** + +````js +abp.ajax({ + type: 'GET', + url: '/api/identity/users', + abpHandleError: false // DESABILITAR O TRATAMENTO AUTOMÁTICO DE ERROS +}).then(function(result){ + console.log(result); +}).catch(function(){ + alert("solicitação falhou :("); +}); +```` + +Se você definir `abpHandleError: false` e não capturar o erro você mesmo, então o erro será ocultado e a solicitação falhará silenciosamente. O `abp.ajax` ainda registra o erro no console do navegador (consulte a seção *Configuração* para substituí-lo). + +## Configuração + +O `abp.ajax` possui uma **configuração global** que você pode personalizar com base em seus requisitos. + +### Opções padrão do AJAX + +O objeto `abp.ajax.defaultOpts` é usado para configurar as opções padrão usadas ao realizar uma chamada AJAX, a menos que você as substitua. O valor padrão deste objeto é mostrado abaixo: + +````js +{ + dataType: 'json', + type: 'POST', + contentType: 'application/json', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + } +} +```` + +Portanto, se você quiser alterar o tipo de solicitação padrão, pode fazer da seguinte forma: + +````js +abp.ajax.defaultOpts.type = 'GET'; +```` + +Escreva este código antes de todo o seu código JavaScript. Normalmente, você deseja colocar essa configuração em um arquivo JavaScript separado e adicioná-lo ao layout usando o [bundle](../Bundling-Minification.md) global. + +### Registrar/Mostrar erros + +As seguintes funções podem ser substituídas para personalizar o registro e a exibição das mensagens de erro: + +* A função `abp.ajax.logError` registra os erros usando o [abp.log.error(...)](Logging.md) por padrão. +* A função `abp.ajax.showError` mostra a mensagem de erro usando o [abp.message.error(...)](Message.md) por padrão. +* A função `abp.ajax.handleErrorStatusCode` trata diferentes códigos de status HTTP e mostra mensagens diferentes com base no código. +* A função `abp.ajax.handleAbpErrorResponse` trata os erros enviados com o formato de erro padrão do ABP. +* A função `abp.ajax.handleNonAbpErrorResponse` trata as respostas de erro não padrão. +* A função `abp.ajax.handleUnAuthorizedRequest` trata as respostas com o código de status `401` e redireciona os usuários para a página inicial da aplicação. + +**Exemplo: Substituir a função `logError`** + +````js +abp.ajax.logError = function(error) { + //... +} +```` + +### Outras opções + +* A função `abp.ajax.ajaxSendHandler` é usada para interceptar as solicitações AJAX e adicionar um token de anti-falsificação ao cabeçalho HTTP. Observe que isso funciona para todas as solicitações AJAX, mesmo se você não usar o `abp.ajax`. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Auth.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Auth.md new file mode 100644 index 0000000000..97a4d2210c --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Auth.md @@ -0,0 +1,23 @@ +# ASP.NET Core MVC / Razor Pages UI: JavaScript Auth API + +A API de autenticação permite que você verifique as permissões (políticas) para o usuário atual no lado do cliente. Dessa forma, você pode mostrar/ocultar partes da interface do usuário condicionalmente ou executar sua lógica no lado do cliente com base nas permissões atuais. + +> Este documento explica apenas a API JavaScript. Consulte o [documento de autorização](../../../Authorization.md) para entender o sistema de autorização e permissão do ABP. + +## Uso básico + +A função `abp.auth.isGranted(...)` é usada para verificar se uma permissão/política foi concedida ou não: + +````js +if (abp.auth.isGranted('DeleteUsers')) { + //TODO: Excluir o usuário +} else { + alert("Você não tem permissão para excluir um usuário!"); +} +```` + +## Outros campos e funções + +* `abp.auth.isAnyGranted(...)`: Recebe um ou mais nomes de permissão/política e retorna `true` se pelo menos um deles foi concedido. +* `abp.auth.areAllGranted(...)`: Recebe um ou mais nomes de permissão/política e retorna `true` se todos eles foram concedidos. +* `abp.auth.grantedPolicies`: Este é um objeto onde as chaves são os nomes de permissão/política. Aqui você pode encontrar os nomes de permissão/política concedidos. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Block-Busy.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Block-Busy.md new file mode 100644 index 0000000000..fbfa661cc8 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Block-Busy.md @@ -0,0 +1,56 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Bloqueio/Busy em JavaScript + +A API de Bloqueio desabilita (bloqueia) a página ou uma parte da página. + +## Uso Básico + +**Exemplo: Bloquear (desabilitar) a página completa** + +````js +abp.ui.block(); +```` + +**Exemplo: Bloquear (desabilitar) um elemento HTML** + +````js +abp.ui.block('#MyContainer'); +```` + +**Exemplo: Habilitar novamente o elemento ou página previamente bloqueados:** + +````js +abp.ui.unblock(); +```` + +## Opções + +O método `abp.ui.block()` pode receber um objeto de opções que pode conter os seguintes campos: + +* `elm`: Um seletor opcional para encontrar o elemento a ser bloqueado (por exemplo, `#MyContainerId`). Se não for fornecido, a página inteira será bloqueada. O seletor também pode ser passado diretamente para o método `block()` como mostrado acima. +* `busy`: Defina como `true` para mostrar um indicador de progresso na área bloqueada. +* `promise`: Um objeto de promessa com callbacks `always` ou `finally`. Isso pode ser útil se você quiser desbloquear automaticamente a área bloqueada quando uma operação adiada for concluída. + +**Exemplo: Bloquear um elemento com indicador de ocupado** + +````js +abp.ui.block({ + elm: '#MySection', + busy: true +}); +```` + +A interface resultante será parecida com a seguinte: + +![ui-busy](../../../images/ui-busy.png) + +## setBusy + +`abp.ui.setBusy(...)` e `abp.ui.clearBusy()` são funções de atalho se você quiser usar o bloqueio com a opção `busy`. + +**Exemplo: Bloquear com ocupado** + +````js +abp.ui.setBusy('#MySection'); +```` + +Então você pode usar `abp.ui.clearBusy();` para reabilitar a área/página ocupada. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/CurrentUser.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/CurrentUser.md new file mode 100644 index 0000000000..49a0757bcb --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/CurrentUser.md @@ -0,0 +1,49 @@ +# ASP.NET Core MVC / Razor Pages UI: API JavaScript do Usuário Atual + +`abp.currentUser` é um objeto que contém informações sobre o usuário atual da aplicação. + +> Este documento explica apenas a API JavaScript. Consulte o documento [CurrentUser](../../../CurrentUser.md) para obter informações sobre o usuário atual no lado do servidor. + +## Usuário Autenticado + +Se o usuário estiver autenticado, este objeto será algo como abaixo: + +````js +{ + isAuthenticated: true, + id: "34f1f4a7-13cc-4b91-84d1-b91c87afa95f", + tenantId: null, + userName: "john", + name: "John", + surName: "Nash", + email: "john.nash@abp.io", + emailVerified: true, + phoneNumber: null, + phoneNumberVerified: false, + roles: ["moderator","supporter"] +} +```` + +Portanto, `abp.currentUser.userName` retorna `john` neste caso. + +## Usuário Anônimo + +Se o usuário não estiver autenticado, este objeto será algo como abaixo: + +````js +{ + isAuthenticated: false, + id: null, + tenantId: null, + userName: null, + name: null, + surName: null, + email: null, + emailVerified: false, + phoneNumber: null, + phoneNumberVerified: false, + roles: [] +} +```` + +Você pode verificar `abp.currentUser.isAuthenticated` para entender se o usuário está autenticado ou não. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/DOM.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/DOM.md new file mode 100644 index 0000000000..b8f325c8b6 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/DOM.md @@ -0,0 +1,116 @@ +# ASP.NET Core MVC / Razor Pages UI: API DOM JavaScript + +`abp.dom` (Document Object Model) fornece eventos aos quais você pode se inscrever para ser notificado quando elementos são adicionados e removidos dinamicamente da página (DOM). + +Isso é especialmente útil se você deseja inicializar os novos elementos carregados. Isso geralmente é necessário quando você adiciona elementos dinamicamente ao DOM (por exemplo, obtém alguns elementos HTML via AJAX) após a inicialização da página. + +> O ABP usa o [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) para observar as alterações feitas no DOM. + +## Eventos de Nó + +### onNodeAdded + +Esse evento é acionado quando um elemento é adicionado ao DOM. Exemplo: + +````js +abp.dom.onNodeAdded(function(args){ + console.log(args.$el); +}); +```` + +O objeto `args` possui os seguintes campos: + +* `$el`: A seleção JQuery para obter o novo elemento inserido no DOM. + +### onNodeRemoved + +Esse evento é acionado quando um elemento é removido do DOM. Exemplo: + +````js +abp.dom.onNodeRemoved(function(args){ + console.log(args.$el); +}); +```` + +O objeto `args` possui os seguintes campos: + +* `$el`: A seleção JQuery para obter o elemento removido do DOM. + +## Inicializadores Pré-Construídos + +O ABP Framework usa os eventos DOM para inicializar algum tipo de elementos HTML quando eles são adicionados ao DOM após a inicialização da página. + +> Observe que os mesmos inicializadores também funcionam se esses elementos já estiverem incluídos no DOM inicial. Portanto, se eles forem carregados inicialmente ou de forma tardia, eles funcionam como esperado. + +### Inicializador de Formulário + +O inicializador de formulário (definido como `abp.dom.initializers.initializeForms`) inicializa os formulários carregados de forma tardia; + +* Habilita automaticamente a validação `unobtrusive` no formulário. +* Pode mostrar automaticamente uma mensagem de confirmação quando você envia o formulário. Para habilitar esse recurso, basta adicionar o atributo `data-confirm` com uma mensagem (como `data-confirm="Tem certeza?"`) ao elemento `form`. +* Se o elemento `form` tiver o atributo `data-ajaxForm="true"`, então chama automaticamente o `.abpAjaxForm()` no elemento `form`, para enviar o formulário via AJAX. + +Consulte o documento [Forms & Validation](../Forms-Validation.md) para mais informações. + +### Inicializador de Script + +O inicializador de script (`abp.dom.initializers.initializeScript`) pode executar um código JavaScript para um elemento DOM. + +**Exemplo: Carregar de forma tardia um componente e executar algum código quando o elemento for carregado** + +Suponha que você tenha um contêiner para carregar o elemento dentro: + +````html +<div id="LazyComponent"></div> +```` + +E este é o componente que será carregado via AJAX do servidor e inserido no contêiner: + +````html +<div data-script-class="MyCustomClass"> + <p>Mensagem de exemplo</p> +</div> +```` + +`data-script-class="MyCustomClass"` indica a classe JavaScript que será usada para executar alguma lógica nesse elemento: + +`MyCustomClass` é um objeto global definido da seguinte forma: + +````js +MyCustomClass = function(){ + + function initDom($el){ + $el.css('color', 'red'); + } + + return { + initDom: initDom + } +}; +```` + +`initDom` é a função que é chamada pelo ABP Framework. O argumento `$el` é o elemento HTML carregado como uma seleção JQuery. + +Finalmente, você pode carregar o componente dentro do contêiner após uma chamada AJAX: + +````js +$(function () { + setTimeout(function(){ + $.get('/get-my-element').then(function(response){ + $('#LazyComponent').html(response); + }); + }, 2000); +}); +```` + +O sistema de Inicialização de Script é especialmente útil se você não sabe como e quando o componente será carregado no DOM. Isso pode ser possível se você desenvolveu um componente de IU reutilizável em uma biblioteca e deseja que o desenvolvedor do aplicativo não precise se preocupar com a inicialização do componente em diferentes casos de uso. + +> A inicialização de script não funciona se o componente for carregado no DOM inicial. Nesse caso, você é responsável por inicializá-lo. + +### Outros Inicializadores + +Os seguintes componentes e bibliotecas do Bootstrap são inicializados automaticamente quando são adicionados ao DOM: + +* Tooltip +* Popover +* Timeago \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Events.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Events.md new file mode 100644 index 0000000000..0d7a1bbd2b --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Events.md @@ -0,0 +1,87 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Eventos JavaScript + +O objeto `abp.event` é um serviço simples que é usado para publicar e se inscrever em eventos globais **no navegador**. + +> Esta API não está relacionada a eventos locais ou distribuídos do lado do servidor. Ela funciona dentro dos limites do navegador para permitir que os componentes da interface do usuário (partes do código) se comuniquem de forma desacoplada. + +## Uso Básico + +### Publicando Eventos + +Use `abp.event.trigger` para publicar eventos. + +**Exemplo: Publicar um evento *Basket Updated* (Carrinho Atualizado)** + +````js +abp.event.trigger('basketUpdated'); +```` + +Isso acionará todas as chamadas de retorno inscritas. + +### Se inscrevendo nos Eventos + +Use `abp.event.on` para se inscrever em eventos. + +**Exemplo: Consumir o evento *Basket Updated* (Carrinho Atualizado)** + +````js +abp.event.on('basketUpdated', function() { + console.log('Manipulou o evento basketUpdated...'); +}); +```` + +Você começará a receber eventos depois de se inscrever no evento. + +### Cancelando a inscrição nos Eventos + +Se você precisar cancelar a inscrição em um evento pré-inscrito, poderá usar a função `abp.event.off(eventName, callback)`. Nesse caso, você tem a chamada de retorno como uma declaração de função separada. + +**Exemplo: Inscrever e cancelar a inscrição** + +````js +function onBasketUpdated() { + console.log('Manipulou o evento basketUpdated...'); +} + +//Inscrever +abp.event.on('basketUpdated', onBasketUpdated); + +//Cancelar a inscrição +abp.event.off('basketUpdated', onBasketUpdated); +```` + +Você não receberá mais eventos depois de cancelar a inscrição no evento. + +## Argumentos do Evento + +Você pode passar argumentos (de qualquer quantidade) para o método `trigger` e obtê-los na chamada de retorno da inscrição. + +**Exemplo: Adicionar o carrinho como argumento do evento** + +````js +//Inscrever-se no evento +abp.event.on('basketUpdated', function(basket) { + console.log('O novo objeto do carrinho: '); + console.log(basket); +}); + +//Acionar o evento +abp.event.trigger('basketUpdated', { + items: [ + { + "productId": "123", + "count": 2 + }, + { + "productId": "832", + "count": 1 + } + ] +}); +```` + +### Múltiplos Argumentos + +Se você deseja passar vários argumentos, pode passar como `abp.event.on('basketUpdated', arg0, arg1, agr2)`. Em seguida, você pode adicionar a mesma lista de argumentos à função de chamada de retorno no lado do assinante. + +> **Dica:** Alternativamente, você pode enviar um único objeto que possui um campo separado para cada argumento. Isso facilita a extensão/mudança dos argumentos do evento no futuro sem quebrar os assinantes. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Features.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Features.md new file mode 100644 index 0000000000..95f31ae181 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Features.md @@ -0,0 +1,29 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Recursos JavaScript + +A API `abp.features` permite que você verifique recursos ou obtenha os valores dos recursos no lado do cliente. Você pode ler o valor atual de um recurso apenas no lado do cliente se for permitido pela definição do recurso (no lado do servidor). + +> Este documento explica apenas a API JavaScript. Consulte o documento [Recursos](../../../Recursos.md) para entender o sistema de Recursos do ABP. + +## Uso Básico + +````js +// Obtém um valor como string. +var valor = abp.features.get('ExportarParaExcel'); + +// Verifica se o recurso está habilitado +var habilitado = abp.features.isEnabled('ExportarParaExcel.Habilitado'); +```` + +## Todos os Valores + +`abp.features.values` pode ser usado para acessar todos os valores dos recursos. + +Um exemplo de valor desse objeto é mostrado abaixo: + +````js +{ + Identity.DoisFatores: "Opcional", + ExportarParaExcel.Habilitado: "true", + ... +} +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/GlobalFeatures.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/GlobalFeatures.md new file mode 100644 index 0000000000..ff6cc7b058 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/GlobalFeatures.md @@ -0,0 +1,24 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Recursos Globais JavaScript + +A API `abp.globalFeatures` permite que você obtenha os recursos habilitados dos [Recursos Globais](../../../Global-Features.md) no lado do cliente. + +> Este documento apenas explica a API JavaScript. Consulte o documento [Recursos Globais](../../../Global-Features.md) para entender o sistema de Recursos Globais do ABP. + +## Uso + +````js +// Obtém todos os recursos globais habilitados. +> abp.globalFeatures.enabledFeatures + +[ 'Shopping.Payment', 'Ecommerce.Subscription' ] + + +// Verifica se o recurso global está habilitado. +> abp.globalFeatures.isEnabled('Ecommerce.Subscription') + +true + +> abp.globalFeatures.isEnabled('My.Subscription') + +false +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Index.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Index.md new file mode 100644 index 0000000000..c6387c43b6 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Index.md @@ -0,0 +1,20 @@ +# API JavaScript + +ABP fornece um conjunto de APIs JavaScript para aplicações ASP.NET Core MVC / Razor Pages. Elas podem ser usadas para realizar facilmente requisitos comuns de aplicativos no lado do cliente e integrar ao lado do servidor. + +## APIs + +* [AJAX](Ajax.md) +* [Auth](Auth.md) +* [CurrentUser](CurrentUser.md) +* [DOM](DOM.md) +* [Events](Events.md) +* [Features](Features.md) +* [Global Features](GlobalFeatures.md) +* [Localization](Localization.md) +* [Logging](Logging.md) +* [ResourceLoader](ResourceLoader.md) +* [Settings](Settings.md) +* [UI Block/Busy](Block-Busy.md) +* [UI Message](Message.md) +* [UI Notification](Notify.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Localization.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Localization.md new file mode 100644 index 0000000000..ea640fc748 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Localization.md @@ -0,0 +1,146 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Localização JavaScript + +A API de localização permite que você reutilize os recursos de localização do lado do servidor no lado do cliente. + +> Este documento explica apenas a API JavaScript. Consulte o [documento de localização](../../../Localization.md) para entender o sistema de localização do ABP. + +## Uso básico + +A função `abp.localization.getResource(...)` é usada para obter um recurso de localização: + +````js +var testResource = abp.localization.getResource('Test'); +```` + +Em seguida, você pode localizar uma string com base nesse recurso: + +````js +var str = testResource('HelloWorld'); +```` + +A função `abp.localization.localize(...)` é um atalho onde você pode especificar tanto o nome do texto quanto o nome do recurso: + +````js +var str = abp.localization.localize('HelloWorld', 'Test'); +```` + +`HelloWorld` é o texto a ser localizado, onde `Test` é o nome do recurso de localização aqui. + +### Lógica de fallback + +Se os textos fornecidos não forem localizados, o método de localização retorna a chave fornecida como resultado da localização. + +### Recurso de Localização Padrão + +Se você não especificar o nome do recurso de localização, ele usará o **recurso de localização padrão** definido nas `AbpLocalizationOptions` (consulte o [documento de localização](../../../Localization.md)). + +**Exemplo: Usando o recurso de localização padrão** + +````js +var str = abp.localization.localize('HelloWorld'); // usa o recurso padrão +```` + +### Argumentos de Formato + +Se sua string localizada contiver argumentos, como `Olá {0}, bem-vindo!`, você pode passar argumentos para os métodos de localização. Exemplos: + +````js +var testSource = abp.localization.getResource('Test'); +var str1 = testSource('HelloWelcomeMessage', 'John'); +var str2 = abp.localization.localize('HelloWelcomeMessage', 'Test', 'John'); +```` + +Supondo que `HelloWelcomeMessage` seja localizado como `Olá {0}, bem-vindo!`, ambos os exemplos acima produzem a saída `Olá John, bem-vindo!`. + +## Outras Propriedades e Métodos + +### abp.localization.resources + +A propriedade `abp.localization.resources` armazena todos os recursos de localização, chaves e seus valores. + +### abp.localization.isLocalized + +Retorna um booleano indicando se o texto fornecido foi localizado ou não. + +**Exemplo** + +````js +abp.localization.isLocalized('ProductName', 'MyResource'); +```` + +Retorna `true` se o texto `ProductName` foi localizado para o recurso `MyResource`. Caso contrário, retorna `false`. Você pode deixar o nome do recurso vazio para usar o recurso de localização padrão. + +### abp.localization.defaultResourceName + +`abp.localization.defaultResourceName` pode ser definido para alterar o recurso de localização padrão. Normalmente, você não define isso, pois o ABP Framework o define automaticamente com base na configuração do lado do servidor. + +### abp.localization.currentCulture + +`abp.localization.currentCulture` retorna um objeto para obter informações sobre o **idioma atualmente selecionado**. + +Um exemplo de valor desse objeto é mostrado abaixo: + +````js +{ + "displayName": "Inglês", + "englishName": "Inglês", + "threeLetterIsoLanguageName": "eng", + "twoLetterIsoLanguageName": "en", + "isRightToLeft": false, + "cultureName": "en", + "name": "en", + "nativeName": "Inglês", + "dateTimeFormat": { + "calendarAlgorithmType": "Calendário Solar", + "dateTimeFormatLong": "dddd, d 'de' MMMM 'de' yyyy", + "shortDatePattern": "dd/MM/yyyy", + "fullDateTimePattern": "dddd, d 'de' MMMM 'de' yyyy HH:mm:ss", + "dateSeparator": "/", + "shortTimePattern": "HH:mm", + "longTimePattern": "HH:mm:ss" + } +} +```` + +### abp.localization.languages + +Usado para obter a lista de todos os **idiomas disponíveis** na aplicação. Um exemplo de valor desse objeto é mostrado abaixo: + +````js +[ + { + "cultureName": "en", + "uiCultureName": "en", + "displayName": "Inglês", + "flagIcon": null + }, + { + "cultureName": "fr", + "uiCultureName": "fr", + "displayName": "Francês", + "flagIcon": null + }, + { + "cultureName": "pt-BR", + "uiCultureName": "pt-BR", + "displayName": "Português", + "flagIcon": null + }, + { + "cultureName": "tr", + "uiCultureName": "tr", + "displayName": "Turco", + "flagIcon": null + }, + { + "cultureName": "zh-Hans", + "uiCultureName": "zh-Hans", + "displayName": "Chinês Simplificado", + "flagIcon": null + } +] +```` + +## Veja também + +* [Tutorial em vídeo](https://abp.io/video-courses/essentials/localization) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Logging.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Logging.md new file mode 100644 index 0000000000..3e4475f58a --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Logging.md @@ -0,0 +1,49 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Registro de JavaScript + +A API `abp.log` é usada para escrever logs simples no lado do cliente. + +> Os logs são escritos no console, usando o `console.log`, por padrão. + +> Este documento é para registro simples no lado do cliente. Consulte o documento [Logging](../../../Logging.md) para o sistema de registro no lado do servidor. + +## Uso Básico + +Use um dos métodos `abp.log.xxx(...)` com base na gravidade da mensagem de log. + +````js +abp.log.debug("Algum log de debug aqui..."); // Registrando uma mensagem de debug simples +abp.log.info({ name: "john", age: 42 }); // Registrando um objeto como um log de informação +abp.log.warn("Uma mensagem de aviso"); // Registrando uma mensagem de aviso +abp.log.error('Ocorreu um erro...'); // Mensagem de erro +abp.log.fatal('A conexão de rede foi perdida!'); // Erro fatal +```` + +## Níveis de Log + +Existem 5 níveis para uma mensagem de log: + +* DEBUG = 1 +* INFO = 2 +* WARN = 3 +* ERROR = 4 +* FATAL = 5 + +Esses são definidos no objeto `abp.log.levels` (como `abp.log.levels.WARN`). + +### Alterando o Nível de Log Atual + +Você pode controlar o nível de log da seguinte forma: + +````js +abp.log.level = abp.log.levels.WARN; +```` + +O nível de log padrão é `DEBUG`. + +### Registrando Especificando o Nível + +Em vez de chamar a função `abp.log.info(...)`, você pode usar o `abp.log.log` especificando o nível de log como parâmetro: + +````js +abp.log.log("mensagem de log...", abp.log.levels.INFO); +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Message.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Message.md new file mode 100644 index 0000000000..7b78f70bff --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Message.md @@ -0,0 +1,127 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Mensagens JavaScript + +A API de Mensagens é usada para mostrar mensagens agradáveis ao usuário como um diálogo bloqueante. A API de Mensagens é uma abstração fornecida pelo ABP Framework e implementada usando a biblioteca [SweetAlert](https://sweetalert.js.org/) por padrão. + +## Exemplo Rápido + +Use a função `abp.message.success(...)` para mostrar uma mensagem de sucesso: + +````js +abp.message.success('Suas alterações foram salvas com sucesso!', 'Parabéns'); +```` + +Isso mostrará um diálogo na interface do usuário: + +![js-message-success](../../../images/js-message-success.png) + +## Mensagens Informativas + +Existem quatro tipos de funções de mensagem informativa: + +* `abp.message.info(...)` +* `abp.message.success(...)` +* `abp.message.warn(...)` +* `abp.message.error(...)` + +Todos esses métodos recebem dois parâmetros: + +* `message`: A mensagem (`string`) a ser mostrada. +* `title`: Um título opcional (`string`). + +**Exemplo: Mostrar uma mensagem de erro** + +````js +abp.message.error('O número do seu cartão de crédito não é válido!'); +```` + +![js-message-error](../../../images/js-message-error.png) + +## Mensagem de Confirmação + +A função `abp.message.confirm(...)` pode ser usada para obter uma confirmação do usuário. + +**Exemplo** + +Use o seguinte código para obter um resultado de confirmação do usuário: + +````js +abp.message.confirm('Tem certeza de que deseja excluir a função "admin"?') +.then(function(confirmed){ + if(confirmed){ + console.log('TODO: excluindo a função...'); + } +}); +```` + +A interface resultante será como mostrado abaixo: + +![js-message-confirm](../../../images/js-message-confirm.png) + +Se o usuário clicou no botão `Sim`, o argumento `confirmed` na função de retorno `then` será `true`. + +> "*Tem certeza?*" é o título padrão (localizado com base no idioma atual) e você pode substituí-lo. + +### O Valor de Retorno + +O valor de retorno da função `abp.message.confirm(...)` é uma promessa, então você pode encadear um retorno de chamada `then` como mostrado acima. + +### Parâmetros + +A função `abp.message.confirm(...)` possui os seguintes parâmetros: + +* `message`: Uma mensagem (string) para mostrar ao usuário. +* `titleOrCallback` (opcional): Um título ou uma função de retorno de chamada. Se você fornecer uma string, ela será mostrada como título. Se você fornecer uma função de retorno de chamada (que recebe um parâmetro `bool`), ela será chamada com o resultado. +* `callback` (opcional): Se você passou um título para o segundo parâmetro, pode passar sua função de retorno de chamada como o terceiro parâmetro. + +Passar uma função de retorno de chamada é uma alternativa ao retorno de chamada `then` mostrado acima. + +**Exemplo: Fornecendo todos os parâmetros e obtendo o resultado com a função de retorno de chamada** + +````js +abp.message.confirm( + 'Tem certeza de que deseja excluir a função "admin"?', + 'Cuidado!', + function(confirmed){ + if(confirmed){ + console.log('TODO: excluindo a função...'); + } + }); +```` + +## Configuração do SweetAlert + +A API de Mensagens é implementada usando a biblioteca [SweetAlert](https://sweetalert.js.org/) por padrão. Se você deseja alterar sua configuração, pode definir as opções no objeto `abp.libs.sweetAlert.config`. O objeto de configuração padrão é mostrado abaixo: + +````js +{ + 'default': { + }, + info: { + icon: 'info' + }, + success: { + icon: 'success' + }, + warn: { + icon: 'warning' + }, + error: { + icon: 'error' + }, + confirm: { + icon: 'warning', + title: 'Tem certeza?', + buttons: ['Cancelar', 'Sim'] + } +} +```` + +> "Tem certeza?", "Cancelar" e "Sim" são textos automaticamente localizados com base no idioma atual. + +Portanto, se você deseja definir o ícone `warn`, pode definir assim: + +````js +abp.libs.sweetAlert.config.warn.icon = 'error'; +```` + +Consulte a [documentação do SweetAlert](https://sweetalert.js.org/) para todas as opções de configuração. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Notify.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Notify.md new file mode 100644 index 0000000000..45087efb8a --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Notify.md @@ -0,0 +1,45 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Notificação JavaScript + +A API de Notificação é usada para mostrar notificações de interface do usuário no estilo toast, que desaparecem automaticamente para o usuário final. Por padrão, ela é implementada pela biblioteca [Toastr](https://github.com/CodeSeven/toastr). + +## Exemplo Rápido + +Use a função `abp.notify.success(...)` para mostrar uma mensagem de sucesso: + +````js +abp.notify.success( + 'O produto "Acme Atom Re-Arranger" foi excluído com sucesso.', + 'Produto Excluído' +); +```` + +Uma mensagem de notificação é exibida na parte inferior direita da página: + +![js-message-success](../../../images/js-notify-success.png) + +## Tipos de Notificação + +Existem quatro tipos de notificações pré-definidas; + +* `abp.notify.success(...)` +* `abp.notify.info(...)` +* `abp.notify.warn(...)` +* `abp.notify.error(...)` + +Todos os métodos acima recebem os seguintes parâmetros; + +* `message`: Uma mensagem (`string`) para mostrar ao usuário. +* `title`: Um título opcional (`string`). +* `options`: Opções adicionais a serem passadas para a biblioteca subjacente, para o Toastr por padrão. + +## Configuração do Toastr + +A API de notificação é implementada pela biblioteca [Toastr](https://github.com/CodeSeven/toastr) por padrão. Você pode ver suas próprias opções de configuração. + +**Exemplo: Mostrar mensagens toast no canto superior direito da página** + +````js +toastr.options.positionClass = 'toast-top-right'; +```` + +> O ABP define essa opção como `toast-bottom-right` por padrão. Você pode substituí-la como mostrado acima. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/ResourceLoader.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/ResourceLoader.md new file mode 100644 index 0000000000..3e7d0d6e2c --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/ResourceLoader.md @@ -0,0 +1,39 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Carregamento de Recursos JavaScript + +`abp.ResourceLoader` é um serviço que pode carregar um arquivo JavaScript ou CSS sob demanda. Ele garante que o arquivo seja carregado apenas uma vez, mesmo que seja solicitado várias vezes. + +## Carregando Arquivos de Script + +A função `abp.ResourceLoader.loadScript(...)` **carrega** um arquivo JavaScript do servidor e o **executa**. + +**Exemplo: Carregar um arquivo JavaScript** + +````js +abp.ResourceLoader.loadScript('/Pages/my-script.js'); +```` + +### Parâmetros + +A função `loadScript` pode receber três parâmetros; + +* `url` (obrigatório, `string`): A URL do arquivo de script a ser carregado. +* `loadCallback` (opcional, `function`): Uma função de retorno de chamada que é chamada assim que o script é carregado e executado. Nessa função de retorno de chamada, você pode usar com segurança o código no arquivo de script. Essa função de retorno de chamada é chamada mesmo se o arquivo já tiver sido carregado anteriormente. +* `failCallback` (opcional, `function`): Uma função de retorno de chamada que é chamada se o carregamento do script falhar. + +**Exemplo: Fornecer o argumento `loadCallback`** + +````js +abp.ResourceLoader.loadScript('/Pages/my-script.js', function() { + console.log('carregado com sucesso :)'); +}); +```` + +## Carregando Arquivos de Estilo + +A função `abp.ResourceLoader.loadStyle(...)` adiciona um elemento `link` à `head` do documento para a URL fornecida, para que o arquivo CSS seja carregado automaticamente pelo navegador. + +**Exemplo: Carregar um arquivo CSS** + +````js +abp.ResourceLoader.loadStyle('/Pages/my-styles.css'); +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/JavaScript-API/Settings.md b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Settings.md new file mode 100644 index 0000000000..b721085508 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/JavaScript-API/Settings.md @@ -0,0 +1,32 @@ +# ASP.NET Core MVC / Razor Pages UI: API de Configuração JavaScript + +A API de localização permite que você obtenha os valores das configurações no lado do cliente. Você pode ler o valor atual de uma configuração apenas no lado do cliente se isso for permitido pela definição da configuração (no lado do servidor). + +> Este documento explica apenas a API JavaScript. Consulte o [documento de configurações](../../../Settings.md) para entender o sistema de configurações do ABP. + +## Uso básico + +````js +// Obtém um valor como string. +var language = abp.setting.get('Abp.Localization.DefaultLanguage'); + +// Obtém um valor inteiro. +var requiredLength = abp.setting.getInt('Abp.Identity.Password.RequiredLength'); + +// Obtém um valor booleano. +var requireDigit = abp.setting.getBoolean('Abp.Identity.Password.RequireDigit'); +```` + +## Todos os valores + +`abp.setting.values` pode ser usado para obter todos os valores de configuração como um objeto, onde as propriedades do objeto são os nomes das configurações e os valores das propriedades são os valores das configurações. + +Um exemplo de valor desse objeto é mostrado abaixo: + +````js +{ + Abp.Localization.DefaultLanguage: "en", + Abp.Timing.TimeZone: "UTC", + ... +} +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Layout-Hooks.md b/docs/pt-BR/UI/AspNetCore/Layout-Hooks.md new file mode 100644 index 0000000000..09bc9a29b4 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Layout-Hooks.md @@ -0,0 +1,105 @@ +# ASP.NET Core MVC / Razor Pages: Hooks de Layout + +O sistema de temas do ABP Framework coloca o layout da página nos pacotes NuGet do [tema](Theming.md). Isso significa que a aplicação final não inclui um `Layout.cshtml`, portanto, você não pode alterar diretamente o código do layout para personalizá-lo. + +Você copia o código do tema para a sua solução. Nesse caso, você tem total liberdade para personalizá-lo. No entanto, você não poderá obter atualizações automáticas do tema (atualizando o pacote NuGet do tema). + +O ABP Framework oferece diferentes maneiras de [personalizar a interface do usuário](Customization-User-Interface.md). + +O **Sistema de Hooks de Layout** permite que você **adicione código** em partes específicas do layout. Todos os layouts de todos os temas devem implementar esses hooks. Por fim, você pode adicionar um **componente de visualização** em um ponto de hook. + +## Exemplo: Adicionar Script do Google Analytics + +Suponha que você precise adicionar o script do Google Analytics ao layout (que estará disponível para todas as páginas). Primeiro, **crie um componente de visualização** em seu projeto: + +![bookstore-google-analytics-view-component](../../images/bookstore-google-analytics-view-component.png) + +**NotificationViewComponent.cs** + +````csharp +public class GoogleAnalyticsViewComponent : AbpViewComponent +{ + public IViewComponentResult Invoke() + { + return View("/Pages/Shared/Components/GoogleAnalytics/Default.cshtml"); + } +} +```` + +**Default.cshtml** + +````html +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-xxxxxx-1', 'auto'); + ga('send', 'pageview'); +</script> +```` + +Altere `UA-xxxxxx-1` pelo seu próprio código. + +Em seguida, você pode adicionar esse componente a qualquer um dos pontos de hook no `ConfigureServices` do seu módulo: + +````csharp +Configure<AbpLayoutHookOptions>(options => +{ + options.Add( + LayoutHooks.Head.Last, //O nome do hook + typeof(GoogleAnalyticsViewComponent) //O componente a ser adicionado + ); +}); +```` + +Agora, o código do GA será inserido no `head` da página como o último item. + +### Especificando o Layout + +A configuração acima adiciona o `GoogleAnalyticsViewComponent` a todos os layouts. Talvez você queira adicionar apenas a um layout específico: + +````csharp +Configure<AbpLayoutHookOptions>(options => +{ + options.Add( + LayoutHooks.Head.Last, + typeof(GoogleAnalyticsViewComponent), + layout: StandardLayouts.Application //Defina o layout a ser adicionado + ); +}); +```` + +Consulte a seção *Layouts* abaixo para saber mais sobre o sistema de layout. + +## Pontos de Hook de Layout + +Existem alguns pontos de hook de layout predefinidos. O `LayoutHooks.Head.Last` usado acima foi um deles. Os pontos de hook padrão são: + +* `LayoutHooks.Head.First`: Usado para adicionar um componente como o primeiro item na tag HTML head. +* `LayoutHooks.Head.Last`: Usado para adicionar um componente como o último item na tag HTML head. +* `LayoutHooks.Body.First`: Usado para adicionar um componente como o primeiro item na tag HTML body. +* `LayoutHooks.Body.Last`: Usado para adicionar um componente como o último item na tag HTML body. +* `LayoutHooks.PageContent.First`: Usado para adicionar um componente logo antes do conteúdo da página (o `@RenderBody()` no layout). +* `LayoutHooks.PageContent.Last`: Usado para adicionar um componente logo após o conteúdo da página (o `@RenderBody()` no layout). + +> Você (ou os módulos que você está usando) pode adicionar **vários itens ao mesmo ponto de hook**. Todos eles serão adicionados ao layout na ordem em que foram adicionados. + +## Layouts + +O sistema de layout permite que os temas definam layouts padrão e nomeados e permite que qualquer página selecione um layout adequado para o seu propósito. Existem três layouts predefinidos: + +* "**Application**": O layout principal (e padrão) para uma aplicação. Normalmente, contém cabeçalho, menu (barra lateral), rodapé, barra de ferramentas, etc. +* "**Account**": Este layout é usado pelo login, registro e outras páginas semelhantes. É usado para as páginas na pasta `/Pages/Account` por padrão. +* "**Empty**": Layout vazio e mínimo. + +Esses nomes são definidos na classe `StandardLayouts` como constantes. Você pode criar seus próprios layouts, mas esses são os nomes de layout padrão e implementados por todos os temas por padrão. + +### Localização do Layout + +Você pode encontrar os arquivos de layout [aqui](https://github.com/abpframework/abp/blob/dev/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts) para o tema básico. Você pode usá-los como referência para construir seus próprios layouts ou pode substituí-los, se necessário. + +## Veja também + +* [Personalizando a Interface do Usuário](Customization-User-Interface.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Modals.md b/docs/pt-BR/UI/AspNetCore/Modals.md new file mode 100644 index 0000000000..ca4e87b5ab --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Modals.md @@ -0,0 +1,412 @@ +# ASP.NET Core MVC / Razor Pages UI: Modais + +Embora você possa continuar usando o [método padrão do Bootstrap](https://getbootstrap.com/docs/4.5/components/modal/) para criar, abrir e gerenciar modais em suas aplicações, o ABP Framework fornece uma maneira **flexível** de gerenciar modais, automatizando tarefas comuns para você. + +**Exemplo: Um diálogo modal para criar uma nova entidade de função** + +![modal-manager-example-modal](../../images/modal-manager-example-modal.png) + +O ABP Framework oferece os seguintes benefícios para um modal com um formulário dentro dele: + +* **Carrega preguiçosamente** o HTML do modal na página e o **remove** do DOM assim que for fechado. Isso facilita o consumo de um diálogo modal reutilizável. Além disso, toda vez que você abrir o modal, ele será um novo modal, para que você não precise lidar com a redefinição do conteúdo do modal. +* **Dá foco automaticamente** ao primeiro campo de entrada do formulário assim que o modal for aberto. Você também pode especificar isso usando uma `function` ou um seletor `jquery`. +* Determina automaticamente o **formulário** dentro de um modal e envia o formulário via **AJAX** em vez de uma postagem normal da página. +* Verifica automaticamente se o formulário dentro do modal **foi alterado, mas não salvo**. Ele avisa o usuário nesse caso. +* Desabilita automaticamente os botões do modal (salvar e cancelar) até que a operação AJAX seja concluída. +* Facilita o registro de um **objeto JavaScript que é inicializado** assim que o modal é carregado. + +Portanto, você escreve menos código ao lidar com os modais, especialmente os modais com um formulário dentro. + +## Uso básico + +### Criando um modal como uma página Razor + +Para demonstrar o uso, estamos criando uma página Razor simples, chamada `ProductInfoModal.cshtml`, na pasta `/Pages/Products`: + +![modal-page-on-rider](../../images/modal-page-on-rider.png) + +**Conteúdo do ProductInfoModal.cshtml:** + +````html +@page +@model MyProject.Web.Pages.Products.ProductInfoModalModel +@{ + Layout = null; +} +<abp-modal> + <abp-modal-header title="Informações do Produto"></abp-modal-header> + <abp-modal-body> + <h3>@Model.ProductName</h3> + <div> + <img src="@Model.ProductImageUrl" /> + </div> + <p> + @Model.ProductDescription + </p> + <p> + <small><i>Referência: https://acme.com/catalog/</i></small> + </p> + </abp-modal-body> + <abp-modal-footer buttons="Fechar"></abp-modal-footer> +</abp-modal> +```` + +* Esta página define o `Layout` como `null`, pois mostraremos isso como um modal. Portanto, não é necessário envolvê-lo com um layout. +* Ele usa o [tag helper abp-modal](Tag-Helpers/Modals.md) para simplificar a criação do código HTML do modal. Você pode usar o código padrão do modal do Bootstrap se preferir. + +**Conteúdo do ProductInfoModalModel.cshtml.cs:** + +```csharp +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace MyProject.Web.Pages.Products +{ + public class ProductInfoModalModel : AbpPageModel + { + public string ProductName { get; set; } + + public string ProductDescription { get; set; } + + public string ProductImageUrl { get; set; } + + public void OnGet() + { + ProductName = "Bola de Aço Indestrutível Acme"; + ProductDescription = "A Bola de Aço Indestrutível Acme é completamente indestrutível, não há nada que possa destruí-la!"; + ProductImageUrl = "https://acme.com/catalog/acmeindestructo.jpg"; + } + } +} +``` + +Você pode obter as informações do produto de um banco de dados ou API. Estamos definindo as propriedades codificadas para simplificar. + +### Definindo o Modal Manager + +Depois de ter um modal, você pode abri-lo em qualquer página usando algum código JavaScript simples. + +Primeiro, crie um objeto `abp.ModalManager` definindo o `viewUrl`, no arquivo JavaScript da página que usará o modal: + +````js +var productInfoModal = new abp.ModalManager({ + viewUrl: '/Products/ProductInfoModal' +}); +```` + +> Se você só precisa especificar o `viewUrl`, você pode passá-lo diretamente para o construtor `ModalManager`, como um atalho. Exemplo: `new abp.ModalManager('/Products/ProductInfoModal');` + +### Abrindo o Modal + +Em seguida, abra o modal sempre que precisar: + +````js +productInfoModal.open(); +```` + +Normalmente, você deseja abrir o modal quando algo acontece; Por exemplo, quando o usuário clica em um botão: + +````js +$('#OpenProductInfoModal').click(function(){ + productInfoModal.open(); +}); +```` + +O modal resultante será assim: + +![modal-example-product-info](../../images/modal-example-product-info.png) + +#### Abrindo o Modal com Argumentos + +Ao chamar o método `open()`, o `ModalManager` carrega o HTML do modal solicitando-o do `viewUrl`. Você pode passar alguns **parâmetros de string de consulta** para esta URL ao abrir o modal. + +**Exemplo: Passando o ID do produto ao abrir o modal** + +````js +productInfoModal.open({ + productId: 42 +}); +```` + +Você pode adicionar um parâmetro `productId` ao método `get`: + +````csharp +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace MyProject.Web.Pages.Products +{ + public class ProductInfoModalModel : AbpPageModel + { + //... + + public async Task OnGetAsync(int productId) //Adicione o parâmetro productId + { + //TODO: Obter o produto do banco de dados com o productId fornecido + //... + } + } +} +```` + +Dessa forma, você pode usar o `productId` para consultar o produto em uma fonte de dados. + +## Modais com Formulários + +`abp.ModalManager` lida com várias tarefas comuns (descritas na introdução) quando você deseja usar um formulário dentro do modal. + +### Exemplo de Modal com um Formulário + +Esta seção mostra um exemplo de formulário para criar um novo produto. + +#### Criando a Página Razor + +Para este exemplo, crie uma nova página Razor, chamada `ProductCreateModal.cshtml`, na pasta `/Pages/Products`: + +![product-create-modal-page-on-rider](../../images/product-create-modal-page-on-rider.png) + +**Conteúdo do ProductCreateModal.cshtml:** + +````html +@page +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model MyProject.Web.Pages.Products.ProductCreateModalModel +@{ + Layout = null; +} +<form method="post" action="@Url.Page("/Products/ProductCreateModal")"> + <abp-modal> + <abp-modal-header title="Criar Novo Produto"></abp-modal-header> + <abp-modal-body> + <abp-input asp-for="Product.Name"/> + <abp-input asp-for="Product.Description"/> + <abp-input asp-for="Product.ReleaseDate"/> + </abp-modal-body> + <abp-modal-footer buttons="@AbpModalButtons.Save | @AbpModalButtons.Cancel"></abp-modal-footer> + </abp-modal> +</form> +```` + +* O `abp-modal` foi envolvido pelo `form`. Isso é necessário para colocar os botões `Salvar` e `Cancelar` dentro do formulário. Dessa forma, o botão `Salvar` age como o botão `submit` para o `form`. +* Usamos os [tag helpers abp-input](Tag-Helpers/Form-elements.md) para simplificar a criação dos elementos do formulário. Caso contrário, você precisaria escrever mais HTML. + +**Conteúdo do ProductCreateModalModel.cshtml.cs:** + +```csharp +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace MyProject.Web.Pages.Products +{ + public class ProductCreateModalModel : AbpPageModel + { + [BindProperty] + public PoductCreationDto Product { get; set; } + + public async Task OnGetAsync() + { + //TODO: Lógica de obtenção, se disponível + } + + public async Task<IActionResult> OnPostAsync() + { + //TODO: Salvar o Produto... + + return NoContent(); + } + } +} +``` + +* Esta é uma classe `PageModal` simples. O `[BindProperty]` faz com que o formulário seja vinculado ao modelo quando você envia (submete) o formulário; É o sistema padrão do ASP.NET Core. +* `OnPostAsync` retorna `NoContent` (este método é definido pela classe base `AbpPageModel`). Porque não precisamos de um valor de retorno no lado do cliente, após a operação de envio do formulário. + +**PoductCreationDto:** + +`ProductCreateModalModel` usa uma classe `PoductCreationDto` definida da seguinte forma: + +````csharp +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace MyProject.Web.Pages.Products +{ + public class PoductCreationDto + { + [Required] + [StringLength(128)] + public string Name { get; set; } + + [TextArea(Rows = 4)] + [StringLength(2000)] + public string Description { get; set; } + + [DataType(DataType.Date)] + public DateTime ReleaseDate { get; set; } + } +} +```` + +* O Tag Helper `abp-input` pode entender os atributos de anotação de dados e usá-los para moldar e validar os elementos do formulário. Consulte o documento [tag helpers abp-input](Tag-Helpers/Form-elements.md) para saber mais. + +#### Definindo o Modal Manager + +Novamente, crie um objeto `abp.ModalManager` definindo o `viewUrl`, no arquivo JavaScript da página que usará o modal: + +````js +var productCreateModal = new abp.ModalManager({ + viewUrl: '/Products/ProductCreateModal' +}); +```` + +#### Abrindo o Modal + +Em seguida, abra o modal sempre que precisar: + +````js +productCreateModal.open(); +```` + +Normalmente, você deseja abrir o modal quando algo acontece; Por exemplo, quando o usuário clica em um botão: + +````js +$('#OpenProductCreateModal').click(function(){ + productCreateModal.open(); +}); +```` + +Portanto, o código completo será algo assim (supondo que você tenha um `button` com `id` igual a `OpenProductCreateModal` no lado da visualização): + +```js +$(function () { + + var productCreateModal = new abp.ModalManager({ + viewUrl: '/Products/ProductCreateModal' + }); + + $('#OpenProductCreateModal').click(function () { + productCreateModal.open(); + }); + +}); +``` + +O modal resultante será assim: + +![modal-example-product-create](../../images/modal-example-product-create.png) + +#### Salvando o Modal + +Quando você clica no botão `Salvar`, o formulário é enviado para o servidor. Se o servidor retornar uma **resposta de sucesso**, o evento `onResult` é acionado com alguns argumentos, incluindo a resposta do servidor, e o modal é fechado automaticamente. + +Um exemplo de retorno de chamada que registra os argumentos passados para o método `onResult`: + +````js +productCreateModal.onResult(function(){ + console.log(arguments); +}); +```` + +Se o servidor retornar uma resposta de falha, ele mostra a mensagem de erro retornada pelo servidor e mantém o modal aberto. + +> Consulte a seção *Referência do Modal Manager* abaixo para outros eventos do modal. + +#### Cancelando o Modal + +Se você clicar no botão Cancelar com algumas alterações feitas, mas não salvas, você receberá uma mensagem de aviso como esta: + +![modal-manager-cancel-warning](../../images/modal-manager-cancel-warning.png) + +Se você não deseja essa verificação e mensagem, pode adicionar o atributo `data-check-form-on-close="false"` ao seu elemento `form`. Exemplo: + +````html +<form method="post" + action="@Url.Page("/Products/ProductCreateModal")" + data-check-form-on-close="false"> +```` + +### Validação do Formulário + +`ModalManager` aciona automaticamente a validação do formulário quando você clica no botão `Salvar` ou pressiona a tecla `Enter` no formulário: + +![modal-manager-validation](../../images/modal-manager-validation.png) + +Consulte o documento [Forms & Validation](Forms-Validation.md) para saber mais sobre a validação. + +## Modais com Arquivos de Script + +Você pode precisar executar alguma lógica para o seu modal. Para fazer isso, crie um arquivo JavaScript como abaixo: + +````js +abp.modals.ProductInfo = function () { + + function initModal(modalManager, args) { + var $modal = modalManager.getModal(); + var $form = modalManager.getForm(); + + $modal.find('h3').css('color', 'red'); + + console.log('initialized the modal...'); + }; + + return { + initModal: initModal + }; +}; +```` + +* Este código simplesmente adiciona uma classe `ProductInfo` ao namespace `abp.modals`. A classe `ProductInfo` expõe uma única função pública: `initModal`. +* O método `initModal` é chamado pelo `ModalManager` assim que o HTML do modal é inserido no DOM e está pronto para a lógica de inicialização. +* O parâmetro `modalManager` é o objeto `ModalManager` relacionado a essa instância do modal. Portanto, você pode usar qualquer função nele em seu código. Consulte a seção *Referência do ModalManager*. + +Em seguida, inclua este arquivo na página que você usa o modal: + +````html +<abp-script src="/Pages/Products/ProductInfoModal.js"/> +<abp-script src="/Pages/Products/Index.js"/> +```` + +* Usamos o `abp-script` Tag Helper aqui. Consulte o documento [Bundling & Minification](Bundling-Minification.md) se você quiser entender isso. Você pode usar a tag `script` padrão. Não importa para este caso. + +Por fim, defina a opção `modalClass` ao criar a instância `ModalManager`: + +````js +var productInfoModal = new abp.ModalManager({ + viewUrl: '/Products/ProductInfoModal', + modalClass: 'ProductInfo' //Corresponde a abp.modals.ProductInfo +}); +```` + +### Carregamento Preguiçoso do Arquivo de Script + +Em vez de adicionar o `ProductInfoModal.js` à página em que você usa o modal, você pode configurá-lo para carregar preguiçosamente o arquivo de script quando o modal for aberto pela primeira vez. + +Exemplo: + +````js +var productInfoModal = new abp.ModalManager({ + viewUrl: '/Products/ProductInfoModal', + scriptUrl: '/Pages/Products/ProductInfoModal.js', //URL de carregamento preguiçoso + modalClass: 'ProductInfo' +}); +```` + +* `scriptUrl` é usado para definir a URL para carregar o arquivo de script do modal. +* Nesse caso, você não precisa mais incluir o `ProductInfoModal.js` na página. Ele será carregado sob demanda. + +#### Dica: Bundling & Minification + +Embora o carregamento preguiçoso pareça legal no início, ele requer uma chamada adicional ao servidor quando você abre o modal pela primeira vez. + +Em vez disso, você pode usar o sistema de [Bundling & Minification](Bundling-Minification.md) para criar um pacote (que é um único arquivo minificado na produção) para todos os arquivos de script usados em uma página: + +````html +<abp-script-bundle> + <abp-script src="/Pages/Products/ProductInfoModal.js"/> + <abp-script src="/Pages/Products/Index.js"/> +</abp-script-bundle> +```` + +Isso é eficiente se o arquivo de script não for grande e for aberto com frequência enquanto os usuários usam a página. + +Alternativamente, você pode definir a classe `abp.modals.ProductInfo` no arquivo JavaScript principal da página se o modal for usado apenas e sempre na mesma página. Nesse caso, você não precisa de outro arquivo de script externo. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Navigation-Menu.md b/docs/pt-BR/UI/AspNetCore/Navigation-Menu.md new file mode 100644 index 0000000000..1532ebc3b2 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Navigation-Menu.md @@ -0,0 +1,288 @@ +# ASP.NET Core MVC / Razor Pages UI: Menu de Navegação + +Toda aplicação possui um menu principal que permite aos usuários navegar para páginas/telas da aplicação. Algumas aplicações podem conter mais de um menu em diferentes seções da interface do usuário. + +O ABP Framework é um framework de desenvolvimento de aplicações [modular](../../Module-Development-Basics.md). **Cada módulo pode precisar adicionar itens ao menu**. + +Portanto, o ABP Framework **fornece uma infraestrutura de menu** onde: + +* A aplicação ou os módulos podem adicionar itens a um menu, sem saber como o menu é renderizado. +* O [tema](Theming.md) renderiza corretamente o menu. + +## Adicionando Itens ao Menu + +Para adicionar itens ao menu (ou manipular os itens existentes), você precisa criar uma classe que implemente a interface `IMenuContributor`. + +> O [modelo de inicialização da aplicação](../../Startup-Templates/Application.md) já contém uma implementação do `IMenuContributor`. Portanto, você pode adicionar itens dentro dessa classe em vez de criar uma nova. + +**Exemplo: Adicionar um item de menu *CRM* com subitens *Clientes* e *Pedidos*** + +```csharp +using System.Threading.Tasks; +using MyProject.Localization; +using Volo.Abp.UI.Navigation; + +namespace MyProject.Web.Menus +{ + public class MyProjectMenuContributor : IMenuContributor + { + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.Main) + { + await ConfigureMainMenuAsync(context); + } + } + + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + var l = context.GetLocalizer<MyProjectResource>(); + + context.Menu.AddItem( + new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"]) + .AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Customers", + displayName: l["Menu:Customers"], + url: "/crm/customers") + ).AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Orders", + displayName: l["Menu:Orders"], + url: "/crm/orders") + ) + ); + } + } +} +``` + +* Este exemplo adiciona itens apenas ao menu principal (`StandardMenus.Main`: veja a seção *Menus Padrão* abaixo). +* Ele obtém um `IStringLocalizer` do `context` para [localizar](../../Localization.md) os nomes de exibição dos itens do menu. +* Adiciona os Clientes e Pedidos como filhos do menu CRM. + +Depois de criar um contribuidor de menu, você precisa adicioná-lo às `AbpNavigationOptions` no método `ConfigureServices` do seu módulo: + +````csharp +Configure<AbpNavigationOptions>(options => +{ + options.MenuContributors.Add(new MyProjectMenuContributor()); +}); +```` + +Este exemplo usa algumas chaves de localização como nomes de exibição, que devem ser definidas no arquivo de localização: + +````json +"Menu:CRM": "CRM", +"Menu:Orders": "Pedidos", +"Menu:Customers": "Clientes" +```` + +Consulte o [documento de localização](../../Localization.md) para saber mais sobre a localização. + +Quando você executar a aplicação, verá os itens do menu adicionados ao menu principal: + +![nav-main-menu](../../images/nav-main-menu.png) + +> O menu é renderizado pelo tema de interface do usuário atual. Portanto, a aparência do menu principal pode ser completamente diferente com base no seu tema. + +Aqui estão algumas observações sobre os contribuidores de menu; + +* O ABP Framework chama o método `ConfigureMenuAsync` **sempre que precisar renderizar** o menu. +* Cada item do menu pode ter **filhos**. Portanto, você pode adicionar itens de menu com **profundidade ilimitada** (no entanto, seu tema de interface do usuário pode não suportar profundidade ilimitada). +* Apenas os itens de menu folha normalmente têm `url`s. Quando você clica em um menu pai, seu submenu é aberto ou fechado, você não navega para a `url` de um item de menu pai. +* Se um item de menu não tiver filhos e não tiver uma `url` definida, ele não será renderizado na interface do usuário. Isso simplifica a autorização dos itens do menu: você só autoriza os itens filhos (veja a próxima seção). Se nenhum dos filhos for autorizado, o pai desaparece automaticamente. + +### Propriedades do Item do Menu + +Existem mais opções de um item do menu (o construtor da classe `ApplicationMenuItem`). Aqui está a lista de todas as opções disponíveis; + +* `name` (`string`, obrigatório): O **nome único** do item do menu. +* `displayName` (`string`, obrigatório): Nome de exibição/texto do item do menu. Você pode [localizar](../../Localization.md) isso como mostrado anteriormente. +* `url` (`string`): A URL do item do menu. +* `icon` (`string`): Um nome de ícone. Classes de ícone gratuitas do [Font Awesome](https://fontawesome.com/) são suportadas por padrão. Exemplo: `fa fa-book`. Você pode usar qualquer classe de ícone de fonte CSS, desde que inclua os arquivos CSS necessários em sua aplicação. +* `order` (`int`): A ordem do item do menu. O valor padrão é `1000`. Os itens são classificados pela ordem de adição, a menos que você especifique um valor de ordem. +* `customData` (`Dictionary<string, object>`): Um dicionário que permite armazenar objetos personalizados que você pode associar ao item do menu e usá-lo ao renderizar o item do menu. +* `target` (`string`): Destino do item do menu. Pode ser `null` (padrão), "\_*blank*", "\_*self*", "\_*parent*", "\_*top*" ou um nome de frame para aplicações web. +* `elementId` (`string`): Pode ser usado para renderizar o elemento com um atributo HTML `id` específico. +* `cssClass` (`string`): Classes de string adicionais para o item do menu. +* `groupName` (`string`): Pode ser usado para agrupar itens do menu. + +### Autorização + +Como visto acima, um contribuidor de menu contribui para o menu dinamicamente. Portanto, você pode executar qualquer lógica personalizada ou obter itens do menu de qualquer fonte. + +Um caso de uso é a [autorização](../../Authorization.md). Normalmente, você deseja adicionar itens de menu verificando uma permissão. + +**Exemplo: Verificar se o usuário atual possui uma permissão** + +````csharp +if (await context.IsGrantedAsync("NomeDaMinhaPermissao")) +{ + //...adicionar itens do menu +} +```` + +Para a autorização, você pode usar o método de extensão `RequirePermissions` como atalho. Ele também é mais eficiente, o ABP otimiza a verificação de permissão para todos os itens. + +````csharp +context.Menu.AddItem( + new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"]) + .AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Customers", + displayName: l["Menu:Customers"], + url: "/crm/customers") + .RequirePermissions("MyProject.Crm.Customers") + ).AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Orders", + displayName: l["Menu:Orders"], + url: "/crm/orders") + .RequirePermissions("MyProject.Crm.Orders") + ) +); +```` + +> Você pode usar `context.AuthorizationService` para acessar diretamente o `IAuthorizationService`. + +### Resolvendo Dependências + +`context.ServiceProvider` pode ser usado para resolver qualquer dependência de serviço. + +**Exemplo: Obter um serviço** + +````csharp +var meuServico = context.ServiceProvider.GetRequiredService<IMeuServico>(); +//...usar o serviço +```` + +> Você não precisa se preocupar em liberar/descartar serviços. O ABP Framework cuida disso. + +### O Menu de Administração + +Há um item de menu especial no menu que é adicionado pelo ABP Framework: O menu *Administração*. Ele é normalmente usado pelos módulos de administração pré-construídos [application modules](../../Modules/Index.md): + +![nav-main-menu-administration](../../images/nav-main-menu-administration.png) + +Se você deseja adicionar itens de menu sob o item de menu *Administração*, pode usar o método de extensão `context.Menu.GetAdministration()`: + +````csharp +context.Menu.GetAdministration().AddItem(...) +```` + +### Manipulando os Itens do Menu Existente + +O ABP Framework executa os contribuidores de menu pela [ordem de dependência do módulo](../../Module-Development-Basics.md). Portanto, você pode manipular os itens do menu nos quais sua aplicação ou módulo (direta ou indiretamente) depende. + +**Exemplo: Definir um ícone para o item de menu `Usuários` adicionado pelo [Módulo de Identidade](../../Modules/Identity.md)** + +````csharp +var menuUsuario = context.Menu.FindMenuItem(IdentityMenuNames.Users); +menuUsuario.Icon = "fa fa-users"; +```` + +> `context.Menu` permite acessar todos os itens do menu que foram adicionados pelos contribuidores de menu anteriores. + +### Grupos de Menu + +Você pode definir grupos e associar itens de menu a um grupo. + +Exemplo: + +```csharp +using System.Threading.Tasks; +using MyProject.Localization; +using Volo.Abp.UI.Navigation; + +namespace MyProject.Web.Menus +{ + public class MyProjectMenuContributor : IMenuContributor + { + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.Main) + { + await ConfigureMainMenuAsync(context); + } + } + + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + var l = context.GetLocalizer<MyProjectResource>(); + + context.Menu.AddGroup( + new ApplicationMenuGroup( + name: "Main", + displayName: l["Main"] + ) + ) + context.Menu.AddItem( + new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"], groupName: "Main") + .AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Customers", + displayName: l["Menu:Customers"], + url: "/crm/customers") + ).AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Orders", + displayName: l["Menu:Orders"], + url: "/crm/orders") + ) + ); + } + } +} +``` + +> O tema de interface do usuário decidirá se renderizará ou não os grupos e, se decidir renderizar, a forma como é renderizado depende do tema. Apenas o tema LeptonX implementa o grupo de menu. + +## Menus Padrão + +Um menu é um componente **nomeado**. Uma aplicação pode conter mais de um menu com nomes diferentes e exclusivos. Existem dois menus padrão pré-definidos: + +* `Main`: O menu principal da aplicação. Contém links para as páginas da aplicação. Definido como uma constante: `Volo.Abp.UI.Navigation.StandardMenus.Main`. +* `User`: Menu do perfil do usuário. Definido como uma constante: `Volo.Abp.UI.Navigation.StandardMenus.User`. + +O menu `Main` já foi abordado acima. O menu `User` está disponível quando um usuário faz login: + +![user-menu](../../images/user-menu.png) + +Você pode adicionar itens ao menu `User` verificando o `context.Menu.Name` como mostrado abaixo: + +```csharp +if (context.Menu.Name == StandardMenus.User) +{ + //...adicionar itens +} +``` + +## IMenuManager + +O `IMenuManager` é geralmente usado pelo [tema](Theming.md) de interface do usuário para renderizar os itens do menu na interface do usuário. Portanto, **geralmente você não precisa usar diretamente** o `IMenuManager`. + +**Exemplo: Obtendo os itens do menu `Main`** + +```csharp +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Volo.Abp.UI.Navigation; + +namespace MyProject.Web.Pages +{ + public class IndexModel : PageModel + { + private readonly IMenuManager _menuManager; + + public IndexModel(IMenuManager menuManager) + { + _menuManager = menuManager; + } + + public async Task OnGetAsync() + { + var mainMenu = await _menuManager.GetAsync(StandardMenus.Main); + + foreach (var menuItem in mainMenu.Items) + { + //... + } + } + } +} +``` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Overall.md b/docs/pt-BR/UI/AspNetCore/Overall.md new file mode 100644 index 0000000000..d6791f87b2 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Overall.md @@ -0,0 +1,160 @@ +# ASP.NET Core MVC / Razor Pages UI + +## Introdução + +O ABP Framework oferece uma maneira conveniente e confortável de criar aplicativos da web usando o ASP.NET Core MVC / Razor Pages como framework de interface do usuário. + +> O ABP não oferece uma nova/forma personalizada de desenvolvimento de UI. Você pode continuar usando suas habilidades atuais para criar a UI. No entanto, ele oferece muitos recursos para facilitar o desenvolvimento e ter uma base de código mais sustentável. + +### MVC vs Razor Pages + +O ASP.NET Core oferece dois modelos para o desenvolvimento de UI: + +* **[MVC (Model-View-Controller)](https://docs.microsoft.com/en-us/aspnet/core/mvc/)** é a maneira clássica que existe desde a versão 1.0. Este modelo pode ser usado para criar páginas/componentes de UI e APIs HTTP. +* **[Razor Pages](https://docs.microsoft.com/en-us/aspnet/core/razor-pages/)** foi introduzido com o ASP.NET Core 2.0 como uma nova maneira de criar páginas da web. + +**O ABP Framework suporta ambos** os modelos MVC e Razor Pages. No entanto, é sugerido criar as **páginas de UI com a abordagem Razor Pages** e usar o **modelo MVC para construir APIs HTTP**. Portanto, todos os módulos pré-construídos, exemplos e documentação são baseados nas Razor Pages para o desenvolvimento de UI, enquanto você sempre pode aplicar o padrão MVC para criar suas próprias páginas. + +### Modularidade + +A [modularidade](../../Module-Development-Basics.md) é um dos principais objetivos do ABP Framework. Não é diferente para a UI; É possível desenvolver aplicativos modulares e módulos de aplicativos reutilizáveis com páginas e componentes de UI isolados e reutilizáveis. + +O [modelo de inicialização do aplicativo](../../Startup-Templates/Application.md) vem com alguns módulos de aplicativos pré-instalados. Esses módulos têm suas próprias páginas de UI incorporadas em seus próprios pacotes NuGet. Você não vê o código deles em sua solução, mas eles funcionam como esperado em tempo de execução. + +## Sistema de Temas + +O ABP Framework fornece um completo [sistema de temas](Theming.md) com os seguintes objetivos: + +* Módulos de aplicativos reutilizáveis são desenvolvidos de forma **independente de temas**, para que possam funcionar com qualquer tema de UI. +* O tema de UI é **decidido pelo aplicativo final**. +* O tema é distribuído por meio de pacotes NuGet/NPM, para que seja **facilmente atualizável**. +* O aplicativo final pode **personalizar** o tema selecionado. + +### Temas Atuais + +Atualmente, três temas são **oficialmente fornecidos**: + +* O [Tema Básico](Basic-Theme.md) é o tema minimalista com o estilo Bootstrap simples. É **open source e gratuito**. +* O [Tema Lepton](https://commercial.abp.io/themes) é um tema **comercial** desenvolvido pela equipe principal do ABP e faz parte da licença [ABP Commercial](https://commercial.abp.io/). +* O [Tema LeptonX](https://x.leptontheme.com/) é um tema que possui escolhas [comerciais](https://docs.abp.io/en/commercial/latest/themes/lepton-x/mvc) e [lite](../../Themes/LeptonXLite/AspNetCore.md). + +Também existem alguns temas desenvolvidos pela comunidade para o ABP Framework (você pode pesquisar na web). + +### Bibliotecas Base + +Existem um conjunto de bibliotecas JavaScript/CSS padrão que são pré-instaladas e suportadas por todos os temas: + +- [Twitter Bootstrap](https://getbootstrap.com/) como o framework HTML/CSS fundamental. +- [JQuery](https://jquery.com/) para manipulação do DOM. +- [DataTables.Net](https://datatables.net/) para grades de dados. +- [JQuery Validation](https://jqueryvalidation.org/) para validação do lado do cliente e [unobtrusive](https://github.com/aspnet/jquery-validation-unobtrusive) validation +- [FontAwesome](https://fontawesome.com/) como a biblioteca fundamental de fontes CSS. +- [SweetAlert](https://sweetalert.js.org/) para mostrar mensagens de alerta e caixas de diálogo de confirmação. +- [Toastr](https://github.com/CodeSeven/toastr) para mostrar notificações de toast. +- [Lodash](https://lodash.com/) como uma biblioteca de utilitários. +- [Luxon](https://moment.github.io/luxon/) para operações de data/hora. +- [JQuery Form](https://github.com/jquery-form/form) para formulários AJAX. +- [bootstrap-datepicker](https://github.com/uxsolutions/bootstrap-datepicker) para mostrar seletores de data. +- [Select2](https://select2.org/) para melhores caixas de seleção/combo. +- [Timeago](http://timeago.yarp.com/) para mostrar carimbos de data/hora fuzzy atualizados automaticamente. +- [malihu-custom-scrollbar-plugin](https://github.com/malihu/malihu-custom-scrollbar-plugin) para barras de rolagem personalizadas. + +Você pode usar essas bibliotecas diretamente em seus aplicativos, sem precisar importar manualmente sua página. + +### Layouts + +Os temas fornecem os layouts padrão. Portanto, você tem layouts responsivos com os recursos padrão já implementados. A captura de tela abaixo foi tirada do Layout do Aplicativo do [Tema Básico](Basic-Theme.md): + +![basic-theme-application-layout](../../images/basic-theme-application-layout.png) + +Consulte o documento [Theming](Theming.md) para obter mais opções de layout e outros detalhes. + +### Partes do Layout + +Um layout típico consiste em várias partes. O sistema de [temas](Theming.md) fornece [menus](Navigation-Menu.md), [toolbars](Toolbars.md), [hooks de layout](Layout-Hooks.md) e mais para controlar dinamicamente o layout pelo seu aplicativo e pelos módulos que você está usando. + +## Recursos + +Esta seção destaca alguns dos recursos fornecidos pelo ABP Framework para a UI do ASP.NET Core MVC / Razor Pages. + +### Proxies de Cliente de API JavaScript Dinâmico + +O sistema de Proxies de Cliente de API JavaScript Dinâmico permite que você consuma suas APIs HTTP do lado do servidor a partir do seu código de cliente JavaScript, assim como chamar funções locais. + +**Exemplo: Obter uma lista de autores do servidor** + +````js +acme.bookStore.authors.author.getList({ + maxResultCount: 10 +}).then(function(result){ + console.log(result.items); +}); +```` + +`acme.bookStore.authors.author.getList` é uma função gerada automaticamente que faz internamente uma chamada AJAX para o servidor. + +Consulte o documento [Proxies de Cliente de API JavaScript Dinâmico](Dynamic-JavaScript-Proxies.md) para mais informações. + +### Tag Helpers do Bootstrap + +O ABP torna mais fácil e seguro escrever HTML do Bootstrap. + +**Exemplo: Renderizar um modal do Bootstrap** + +````html +<abp-modal> + <abp-modal-header title="Título do Modal" /> + <abp-modal-body> + Uau, você está lendo este texto em um modal! + </abp-modal-body> + <abp-modal-footer buttons="@(AbpModalButtons.Save|AbpModalButtons.Close)"></abp-modal-footer> +</abp-modal> +```` + +Consulte o documento [Tag Helpers](Tag-Helpers/Index.md) para mais informações. + +### Formulários e Validação + +O ABP fornece os tag helpers `abp-dynamic-form` e `abp-input` para simplificar drasticamente a criação de um formulário totalmente funcional que automatiza a localização, validação e envio AJAX. + +**Exemplo: Use `abp-dynamic-form` para criar um formulário completo com base em um modelo** + +````html +<abp-dynamic-form abp-model="Movie" submit-button="true" /> +```` + +Consulte o documento [Formulários e Validação](Forms-Validation.md) para obter mais detalhes. + +### Agrupamento e Minificação / Bibliotecas do Lado do Cliente + +O ABP fornece um sistema de Agrupamento e Minificação flexível e modular para criar pacotes e minificar arquivos de estilo/script em tempo de execução. + +````html +<abp-style-bundle> + <abp-style src="/libs/bootstrap/css/bootstrap.css" /> + <abp-style src="/libs/font-awesome/css/font-awesome.css" /> + <abp-style src="/libs/toastr/toastr.css" /> + <abp-style src="/styles/my-global-style.css" /> +</abp-style-bundle> +```` + +Além disso, o sistema de Gerenciamento de Pacotes do Lado do Cliente oferece uma maneira modular e consistente de gerenciar dependências de bibliotecas de terceiros. + +Consulte os documentos [Agrupamento e Minificação](Bundling-Minification.md) e [Gerenciamento de Pacotes do Lado do Cliente](Client-Side-Package-Management.md). + +### APIs JavaScript + +As [APIs JavaScript](JavaScript-API/Index.md) fornecem abstrações sólidas para a localização, configurações, permissões, recursos... etc. do lado do servidor. Elas também fornecem uma maneira simples de mostrar mensagens e **notificações** ao usuário. + +### Modais, Alertas, Widgets e Mais + +O ABP Framework fornece muitas soluções integradas para requisitos comuns de aplicativos; + +* O [Sistema de Widgets](Widgets.md) pode ser usado para criar widgets reutilizáveis e criar painéis de controle. +* Os [Alertas de Página](Page-Alerts.md) facilitam a exibição de alertas ao usuário. +* O [Gerenciador de Modais](Modals.md) fornece uma maneira simples de construir e usar modais. +* A integração com [Data Tables](Data-Tables.md) facilita a criação de grades de dados. + +## Personalização + +Existem muitas maneiras de personalizar o tema e as UIs dos módulos pré-construídos. Você pode substituir componentes, páginas, recursos estáticos, pacotes e muito mais. Consulte o [Guia de Personalização da Interface do Usuário](Customization-User-Interface.md) para mais informações. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Page-Alerts.md b/docs/pt-BR/UI/AspNetCore/Page-Alerts.md new file mode 100644 index 0000000000..ae20cecf97 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Page-Alerts.md @@ -0,0 +1,84 @@ +# ASP.NET Core MVC / Razor Pages: Alertas de Página + +É comum mostrar alertas de erro, aviso ou informação para informar o usuário. Um exemplo de alerta de *Interrupção de Serviço* é mostrado abaixo: + +![exemplo-alerta-pagina](../../images/exemplo-alerta-pagina.png) + +## Uso Básico + +Se você herda diretamente ou indiretamente de `AbpPageModel`, você pode usar a propriedade `Alerts` para adicionar alertas que serão renderizados após a conclusão da solicitação. + +**Exemplo: Mostrar um alerta de aviso** + +```csharp +namespace MyProject.Web.Pages +{ + public class IndexModel : MyProjectPageModel //ou herde de AbpPageModel + { + public void OnGet() + { + Alerts.Warning( + text: "Teremos uma interrupção de serviço entre 02:00 e 04:00 em 23 de outubro de 2023!", + title: "Interrupção de Serviço" + ); + } + } +} +``` + +Este uso renderiza um alerta que foi mostrado acima. Se você precisar localizar as mensagens, você sempre pode usar o sistema padrão de [localização](../../Localization.md). + +### Exceções / Estados Inválidos do Modelo + +É comum mostrar alertas quando você manipula manualmente exceções (com declarações try/catch) ou deseja lidar com o caso `!ModelState.IsValid` e avisar o usuário. Por exemplo, o Módulo de Conta mostra um aviso se o usuário inserir um nome de usuário ou senha incorretos: + +![layout-alerta-conta](../../images/layout-alerta-conta.png) + +> Observe que geralmente você não precisa manipular exceções manualmente, pois o ABP Framework fornece um sistema automático de [manipulação de exceções](../../Exception-Handling.md). + +### Tipos de Alerta + +`Warning` é usado para mostrar um alerta de aviso. Outros métodos comuns são `Info`, `Danger` e `Success`. + +Além dos métodos padrão, você pode usar o método `Alerts.Add` passando um `enum` `AlertType` com um desses valores: `Default`, `Primary`, `Secondary`, `Success`, `Danger`, `Warning`, `Info`, `Light`, `Dark`. + +### Dispensável + +Todos os métodos de alerta recebem um parâmetro opcional `dismissible`. O valor padrão é `true`, o que torna a caixa de alerta dispensável. Defina-o como `false` para criar uma caixa de alerta fixa. + +## IAlertManager + +Se você precisar adicionar mensagens de alerta de outra parte do seu código, você pode injetar o serviço `IAlertManager` e usar sua lista `Alerts`. + +**Exemplo: Injetar o `IAlertManager`** + +```csharp +using Volo.Abp.AspNetCore.Mvc.UI.Alerts; +using Volo.Abp.DependencyInjection; + +namespace MyProject.Web.Pages +{ + public class MyService : ITransientDependency + { + private readonly IAlertManager _alertManager; + + public MyService(IAlertManager alertManager) + { + _alertManager = alertManager; + } + + public void Test() + { + _alertManager.Alerts.Add(AlertType.Danger, "Mensagem de teste!"); + } + } +} +``` + +## Notas + +### Requisições AJAX + +O sistema de Alerta de Página foi projetado para ser usado em uma solicitação regular de página completa. Não é para requisições AJAX/partial. Os alertas são renderizados no layout da página, portanto, é necessário atualizar a página inteira. + +Para requisições AJAX, é mais adequado lançar exceções (por exemplo, `UserFriendlyException`). Consulte o documento de [manipulação de exceções](../../Exception-Handling.md). \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Page-Header.md b/docs/pt-BR/UI/AspNetCore/Page-Header.md new file mode 100644 index 0000000000..3197ba5d56 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Page-Header.md @@ -0,0 +1,62 @@ +# ASP.NET Core MVC / Razor Pages: Cabeçalho da Página + +O serviço `IPageLayout` pode ser usado para definir o título da página, o item de menu selecionado e os itens do breadcrumb para uma página. É responsabilidade do [tema](Theming.md) renderar esses elementos na página. + +## IPageLayout + +O `IPageLayout` pode ser injetado em qualquer página/view para definir as propriedades do cabeçalho da página. + +### Título da Página + +O título da página pode ser definido da seguinte forma: + +```csharp +@inject IPageLayout PageLayout +@{ + PageLayout.Content.Title = "Lista de Livros"; +} +``` + +* O título da página é definido na tag HTML `title` (além do [nome da marca/aplicativo](Branding.md)). +* O tema pode renderizar o título da página antes do conteúdo da página (ainda não implementado pelo Tema Básico). + +### Breadcrumb + +> **O [Tema Básico](Basic-Theme.md) atualmente não implementa o breadcrumb.** +> +> O [Tema LeptonX Lite](../../Themes/LeptonXLite/AspNetCore.md) suporta o breadcrumb. + +Itens do breadcrumb podem ser adicionados ao `PageLayout.Content.BreadCrumb`. + +**Exemplo: Adicionar Gerenciamento de Idioma aos itens do breadcrumb.** + +``` +PageLayout.Content.BreadCrumb.Add("Gerenciamento de Idioma"); +``` + +O tema então renderiza o breadcrumb. Um exemplo do resultado renderizado pode ser: + +![exemplo-breadcrumb](../../images/exemplo-breadcrumb.png) + +* O ícone Home é renderizado por padrão. Defina `PageLayout.Content.BreadCrumb.ShowHome` como `false` para ocultá-lo. +* O nome da página atual (obtido do `PageLayout.Content.Title`) é adicionado por padrão como o último item. Defina `PageLayout.Content.BreadCrumb.ShowCurrent` como `false` para ocultá-lo. + +Qualquer item que você adicionar é inserido entre o Home e os itens da página atual. Você pode adicionar quantos itens forem necessários. O método `BreadCrumb.Add(...)` recebe três parâmetros: + +* `text`: O texto a ser exibido para o item do breadcrumb. +* `url` (opcional): Uma URL para navegar, se o usuário clicar no item do breadcrumb. +* `icon` (opcional): Uma classe de ícone (como `fas fa-user-tie` para Font-Awesome) para exibir junto com o `text`. + +### O Item de Menu Selecionado + +> **O [Tema Básico](Basic-Theme.md) atualmente não implementa o item de menu selecionado, pois não é aplicável ao menu superior, que é a única opção para o Tema Básico no momento.** +> +> O [Tema LeptonX Lite](../../Themes/LeptonXLite/AspNetCore.md) suporta o item de menu selecionado. + +Você pode definir o nome do item de menu relacionado a esta página: + +```csharp +PageLayout.Content.MenuItemName = "BookStore.Books"; +``` + +O nome do item de menu deve corresponder a um nome de item de menu único definido usando o sistema de [Navegação / Menu](Navigation-Menu.md). Nesse caso, espera-se que o tema torne o item de menu "ativo" no menu principal. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Page-Toolbar-Extensions.md b/docs/pt-BR/UI/AspNetCore/Page-Toolbar-Extensions.md new file mode 100644 index 0000000000..b2e7cfacd0 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Page-Toolbar-Extensions.md @@ -0,0 +1,162 @@ +# Extensões da Barra de Ferramentas da Página para a Interface do Usuário ASP.NET Core + +O sistema de barra de ferramentas da página permite adicionar componentes à barra de ferramentas de qualquer página. A barra de ferramentas da página é a área à direita do cabeçalho de uma página. Um botão ("Importar usuários do Excel") foi adicionado à página de gerenciamento de usuários abaixo: + +![botão-barra-ferramentas-página](../../images/page-toolbar-button.png) + +Você pode adicionar qualquer tipo de componente de visualização à barra de ferramentas da página ou modificar os itens existentes. + +## Como Configurar + +Neste exemplo, adicionaremos um botão "Importar usuários do Excel" e executaremos um código JavaScript para a página de gerenciamento de usuários do [Módulo de Identidade](../../Modules/Identity.md). + +### Adicionar um Novo Botão à Página de Gerenciamento de Usuários + +Escreva o seguinte código dentro do método `ConfigureServices` da classe do seu módulo web: + +````csharp +Configure<AbpPageToolbarOptions>(options => +{ + options.Configure<Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel>(toolbar => + { + toolbar.AddButton( + LocalizableString.Create<MyProjectNameResource>("ImportFromExcel"), + icon: "file-import", + id: "ImportUsersFromExcel", + type: AbpButtonType.Secondary + ); + }); +}); +```` + +`AddButton` é um atalho para simplesmente adicionar um componente de botão. Observe que você precisa adicionar a chave `ImportFromExcel` ao seu dicionário de localização (arquivo json) para localizar o texto. + +Quando você executar a aplicação, verá o botão adicionado ao lado da lista de botões atual. Existem outros parâmetros do método `AddButton` (por exemplo, use `order` para definir a ordem do componente de botão em relação aos outros componentes). + +### Criar um Arquivo JavaScript + +Agora, podemos ir para o lado do cliente para lidar com o evento de clique do novo botão. Primeiro, adicione um novo arquivo JavaScript à sua solução. Nós adicionamos dentro da pasta `/Pages/Identity/Users` do projeto `.Web`: + +![extensão-ação-usuário-na-solução](../../images/user-action-extension-on-solution.png) + +Aqui está o conteúdo deste arquivo JavaScript: + +````js +$(function () { + $('#ImportUsersFromExcel').click(function (e) { + e.preventDefault(); + alert('TODO: importar usuários do Excel'); + }); +}); +```` + +No evento `click`, você pode fazer qualquer coisa que precise fazer. + +### Adicionar o Arquivo à Página de Gerenciamento de Usuários + +Em seguida, você precisa adicionar este arquivo JavaScript à página de gerenciamento de usuários. Você pode aproveitar o poder do sistema de [Agrupamento e Minificação](Bundling-Minification.md). + +Escreva o seguinte código dentro do método `ConfigureServices` da classe do seu módulo: + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options.ScriptBundles.Configure( + typeof(Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel).FullName, + bundleConfiguration => + { + bundleConfiguration.AddFiles( + "/Pages/Identity/Users/my-user-extensions.js" + ); + }); +}); +```` + +Essa configuração adiciona `my-user-extensions.js` à página de gerenciamento de usuários do Módulo de Identidade. `typeof(Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel).FullName` é o nome do pacote na página de gerenciamento de usuários. Essa é uma convenção comum usada para todos os módulos comerciais do ABP. + +## Casos de Uso Avançados + +Embora você normalmente queira adicionar uma ação de botão à barra de ferramentas da página, é possível adicionar qualquer tipo de componente. + +### Adicionar um Componente de Visualização à Barra de Ferramentas da Página + +Primeiro, crie um novo componente de visualização em seu projeto: + +![componente-personalizado-barra-ferramentas-página](../../images/page-toolbar-custom-component.png) + +Para este exemplo, criamos um componente de visualização `MyToolbarItem` na pasta `/Pages/Identity/Users/MyToolbarItem`. + +Conteúdo de `MyToolbarItemViewComponent.cs`: + +````csharp +public class MyToolbarItemViewComponent : AbpViewComponent +{ + public IViewComponentResult Invoke() + { + return View("~/Pages/Identity/Users/MyToolbarItem/Default.cshtml"); + } +} +```` + +Conteúdo de `Default.cshtml`: + +````xml +<span> + <button type="button" class="btn btn-dark">CLIQUE AQUI</button> +</span> +```` + +* O arquivo `.cshtml` pode conter qualquer tipo de componente(s). É um componente de visualização típico. +* `MyToolbarItemViewComponent` pode injetar e usar qualquer serviço, se necessário. + +Em seguida, você pode adicionar o `MyToolbarItemViewComponent` à página de gerenciamento de usuários: + +````csharp +Configure<AbpPageToolbarOptions>(options => +{ + options.Configure<Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel>( + toolbar => + { + toolbar.AddComponent<MyToolbarItemViewComponent>(); + } + ); +}); +```` + +* Se o seu componente aceitar argumentos (no método `Invoke`/`InvokeAsync`), você pode passá-los para o método `AddComponent` como um objeto anônimo. + +#### Permissões + +Se o seu botão/componente deve estar disponível com base em uma [permissão/política](../../Authorization.md), você pode passar o nome da permissão/política como parâmetro `requiredPolicyName` para os métodos `AddButton` e `AddComponent`. + +### Adicionar um Contribuidor da Barra de Ferramentas da Página + +Se você realizar uma lógica personalizada avançada ao adicionar um item à barra de ferramentas de uma página, pode criar uma classe que implementa a interface `IPageToolbarContributor` ou herda da classe `PageToolbarContributor`: + +````csharp +public class MyToolbarContributor : PageToolbarContributor +{ + public override Task ContributeAsync(PageToolbarContributionContext context) + { + context.Items.Insert(0, new PageToolbarItem(typeof(MyToolbarItemViewComponent))); + + return Task.CompletedTask; + } +} +```` + +* Você pode usar `context.ServiceProvider` para resolver dependências, se necessário. + +Em seguida, adicione sua classe à lista `Contributors`: + +````csharp +Configure<AbpPageToolbarOptions>(options => +{ + options.Configure<Volo.Abp.Identity.Web.Pages.Identity.Users.IndexModel>( + toolbar => + { + toolbar.Contributors.Add(new MyToolbarContributor()); + } + ); +}); +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Security-Headers.md b/docs/pt-BR/UI/AspNetCore/Security-Headers.md new file mode 100644 index 0000000000..7f2000921c --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Security-Headers.md @@ -0,0 +1,99 @@ +# Cabeçalhos de Segurança + +O ABP Framework permite que você adicione cabeçalhos de segurança frequentemente usados em sua aplicação. Os seguintes cabeçalhos de segurança serão adicionados como cabeçalhos de resposta à sua aplicação se você usar o middleware `UseAbpSecurityHeaders`: + +* `X-Content-Type-Options`: Informa ao navegador para não tentar adivinhar qual pode ser o tipo MIME de um recurso e apenas aceitar o tipo MIME retornado pelo servidor. +* `X-XSS-Protection`: Esta é uma funcionalidade do Internet Explorer, Chrome e Safari que impede o carregamento de páginas quando detectam ataques de cross-site scripting (XSS) refletidos. +* `X-Frame-Options`: Este cabeçalho pode ser usado para indicar se um navegador deve ou não ser permitido a renderizar uma página em uma tag `<iframe>`. Ao especificar o valor desse cabeçalho como *SAMEORIGIN*, você pode fazer com que a página seja exibida em um frame na mesma origem da própria página. +* `Content-Security-Policy`: Este cabeçalho de resposta permite restringir quais recursos (como JavaScript, CSS, imagens, manifestos, etc.) podem ser carregados e os URLs de onde eles podem ser carregados. Esse cabeçalho de segurança só será adicionado se você configurar a classe `AbpSecurityHeadersOptions` e ativá-lo. + +## Configuração + +### AbpSecurityHeadersOptions + +`AbpSecurityHeadersOptions` é a classe principal para habilitar o cabeçalho `Content-Security-Policy`, definir seu valor e adicionar outros cabeçalhos de segurança que você deseja adicionar à sua aplicação. + +**Exemplo:** + +```csharp +Configure<AbpSecurityHeadersOptions>(options => +{ + options.UseContentSecurityPolicyHeader = true; //false por padrão + options.ContentSecurityPolicyValue = "object-src 'none'; form-action 'self'; frame-ancestors 'none'"; //valor padrão + + //adicionando cabeçalhos de segurança adicionais + options.Headers["Referrer-Policy"] = "no-referrer"; +}); +``` + +> Se o cabeçalho for o mesmo, os cabeçalhos de segurança adicionais que você definiu têm precedência sobre os cabeçalhos de segurança padrão. Em outras palavras, eles substituem os valores padrão dos cabeçalhos de segurança. + +## Middleware de Cabeçalhos de Segurança + +O middleware de Cabeçalhos de Segurança é um middleware da pipeline de solicitação do ASP.NET Core que adiciona cabeçalhos de segurança predefinidos à sua aplicação, incluindo `X-Content-Type-Options`, `X-XSS-Protection` e `X-Frame-Options`. Além disso, esse middleware também inclui esses cabeçalhos de segurança exclusivos em sua aplicação se você configurar a classe `AbpSecurityHeadersOptions` conforme mencionado acima. + +**Exemplo:** + +```csharp +app.UseAbpSecurityHeaders(); +``` + +> Você pode adicionar esse middleware após `app.UseRouting()` no método `OnApplicationInitialization` da classe do seu módulo para registrá-lo na pipeline de solicitação. Esse middleware já está configurado nos [ABP Commercial Startup Templates](https://docs.abp.io/en/commercial/latest/startup-templates/index), portanto, você não precisa adicioná-lo manualmente se estiver usando um desses modelos de inicialização. + +Depois de registrar o middleware `UseAbpSecurityHeaders` na pipeline de solicitação, os cabeçalhos de segurança definidos serão exibidos nos cabeçalhos de resposta, como na figura abaixo: + +![](../../images/security-response-headers.png) + +## Nonce de Script da Política de Segurança de Conteúdo + +O Abp Framework fornece uma propriedade para adicionar um valor de nonce dinâmico ao cabeçalho Content-Security-Policy. Com esse recurso, ele adiciona automaticamente um valor de nonce dinâmico ao lado do cabeçalho. E com a ajuda do helper de tag de script, ele adiciona esse valor de [`nonce de script`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) às tags de script em suas páginas (o `ScriptNonceTagHelper` no namespace `Volo.Abp.AspNetCore.Mvc.UI.Bundling` deve ser anexado como um taghelper). +> Se você precisar adicionar manualmente o nonce de script, pode usar 'Html.GetScriptNonce()' para adicionar o valor de nonce ou 'Html.GetScriptNonceAttribute()' para adicionar o valor do atributo nonce. + +Esse recurso está desabilitado por padrão. Você pode ativá-lo definindo a propriedade `UseContentSecurityPolicyScriptNonce` da classe `AbpSecurityHeadersOptions` como `true`. + +### Ignorar Nonce de Script + +Você pode ignorar o nonce de script para algumas páginas ou alguns seletores. Você pode usar as propriedades `IgnoredScriptNoncePaths` e `IgnoredScriptNonceSelectors` da classe `AbpSecurityHeadersOptions`. + +**Exemplo:** + +```csharp +Configure<AbpSecurityHeadersOptions>(options => +{ + //adicionando nonce de script-src + options.UseContentSecurityPolicyScriptNonce = true; //false por padrão + + //ignorar a origem do nonce de script para esses caminhos + options.IgnoredScriptNoncePaths.Add("/minha-pagina"); + + //ignorar o nonce de script por Elsa Workflows e outros seletores + options.IgnoredScriptNonceSelectors.Add(context => + { + var endpoint = context.GetEndpoint(); + return Task.FromResult(endpoint?.Metadata.GetMetadata<PageRouteMetadata>()?.RouteTemplate == "/{SUA_PAGINA_PRINCIPAL}"); + }); +}); +``` + +### Ignorar Cabeçalhos de Segurança do Abp + +Você pode ignorar os Cabeçalhos de Segurança do Abp para algumas ações ou páginas. Você pode usar o atributo `IgnoreAbpSecurityHeaderAttribute` para isso. + +**Exemplo:** + +```csharp +@using Volo.Abp.AspNetCore.Security +@attribute [IgnoreAbpSecurityHeaderAttribute] +``` + +**Exemplo:** + +```csharp +[IgnoreAbpSecurityHeaderAttribute] +public class IndexModel : AbpPageModel +{ + public void OnGet() + { + } +} +``` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Static-JavaScript-Proxies.md b/docs/pt-BR/UI/AspNetCore/Static-JavaScript-Proxies.md new file mode 100644 index 0000000000..9191103e07 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Static-JavaScript-Proxies.md @@ -0,0 +1,157 @@ +# Proxies de Cliente Estático da API JavaScript + +É comum consumir suas APIs HTTP a partir do seu código JavaScript. Para fazer isso, normalmente você lida com chamadas AJAX de baixo nível, como $.ajax, ou melhor [abp.ajax](JavaScript-API/Ajax.md). O ABP Framework fornece **uma maneira melhor** de chamar suas APIs HTTP a partir do seu código JavaScript: Proxies de Cliente da API JavaScript! + +## Proxies de Cliente JavaScript Estáticos vs Dinâmicos + +O ABP fornece **dois tipos** de sistema de geração de proxy de cliente. Este documento explica os **proxies de cliente estáticos**, que geram código do lado do cliente durante o desenvolvimento. Você também pode ver a documentação [Proxies de Cliente JavaScript Dinâmicos da API](Dynamic-JavaScript-Proxies.md) para aprender como usar proxies gerados em tempo de execução. + +A geração de proxy de cliente em tempo de desenvolvimento (estático) tem uma **ligeira vantagem de desempenho**, pois não precisa obter a definição da API HTTP em tempo de execução. No entanto, você deve **regenerar** o código do proxy do cliente sempre que alterar a definição do ponto de extremidade da API. Por outro lado, os proxies de cliente dinâmicos são gerados em tempo de execução e oferecem uma **experiência de desenvolvimento mais fácil**. + +## Um Exemplo Rápido + +### O Serviço de Aplicativo + +Suponha que você tenha um serviço de aplicativo definido como mostrado abaixo: + +````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<AuthorDto> GetAsync(Guid id); + + Task<PagedResultDto<AuthorDto>> GetListAsync(GetAuthorListDto input); + + Task<AuthorDto> CreateAsync(CreateAuthorDto input); + + Task UpdateAsync(Guid id, UpdateAuthorDto input); + + Task DeleteAsync(Guid id); + } +} +```` + +> Você pode seguir o [tutorial de desenvolvimento de aplicativos da web](../../Tutorials/Part-1.md) para aprender como criar [serviços de aplicativos](../../Application-Services.md), expô-los como [APIs HTTP](../../API/Auto-API-Controllers.md) e consumir do código JavaScript como um exemplo completo. + +### Gerando o Código JavaScript + +O lado do servidor deve estar em execução ao gerar o código do proxy do cliente. Portanto, execute primeiro a aplicação que hospeda suas APIs HTTP (pode ser a aplicação Web ou a aplicação HttpApi.Host, dependendo da estrutura da sua solução). + +Abra um terminal de linha de comando na pasta raiz do seu projeto da web (`.csproj`) e digite o seguinte comando: + +````bash +abp generate-proxy -t js -u https://localhost:53929/ +```` + +> Se você ainda não instalou, deve instalar o [ABP CLI](../../CLI.md). Altere a URL de exemplo para a URL raiz da sua aplicação. + +Este comando deve gerar os seguintes arquivos na pasta `ClientProxies`: + +![static-js-proxy-example](../../images/static-js-proxy-example.png) + +`app-proxy.js` é o arquivo de proxy gerado neste exemplo. Aqui, um exemplo de função de proxy neste arquivo: + +````js +acme.bookStore.authors.author.get = function(id, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/app/author/' + id + '', + type: 'GET' + }, ajaxParams)); +}; +```` + +> O comando `generate-proxy` gera proxies apenas para as APIs que você definiu em sua aplicação (assume `app` como o nome do módulo). Se você está desenvolvendo uma aplicação modular, pode especificar o parâmetro `-m` (ou `--module`) para especificar o módulo para o qual deseja gerar proxies. Consulte a seção *generate-proxy* na documentação do [ABP CLI](../CLI.md) para outras opções. + +### Usando as Funções de Proxy + +Para usar as funções de proxy, primeiro importe o arquivo `app-proxy.js` para a sua página: + +````html +<abp-script src="/client-proxies/app-proxy.js"/> +```` + +> Usamos o [abp-script tag helper](Bundling-Minification.md) neste exemplo. Você pode usar a tag `script` padrão, mas o `abp-script` é a maneira recomendada de importar arquivos JavaScript para suas páginas. + +Agora, você pode chamar qualquer um dos métodos do serviço de aplicativo a partir do seu código JavaScript, assim como chamar uma função JavaScript. A função JavaScript tem o mesmo **nome**, **parâmetros** e **valor de retorno** do método C#. + +**Exemplo: Obter um único autor** + +````js +acme.bookStore.authors.author + .get("7245a066-5457-4941-8aa7-3004778775f0") //Obtenha o id de algum lugar! + .then(function(result){ + console.log(result); + }); +```` + +**Exemplo: Obter a lista de autores** + +````js +acme.bookStore.authors.author.getList({ + maxResultCount: 10 +}).then(function(result){ + console.log(result.items); +}); +```` + +**Exemplo: Excluir um autor** + +```js +acme.bookStore.authors.author + .delete('7245a066-5457-4941-8aa7-3004778775f0') //Obtenha o id de algum lugar! + .then(function() { + abp.notify.info('Excluído com sucesso!'); + }); +``` + +## Desabilitando Proxies JavaScript Dinâmicos + +Quando você cria um aplicativo ou módulo, a abordagem de [geração dinâmica de proxy de cliente](Dynamic-JavaScript-Proxies.md) é usada por padrão. Se você deseja usar os proxies de cliente gerados estaticamente para o seu aplicativo, você deve desabilitá-lo explicitamente para o seu aplicativo ou módulo no método `ConfigureServices` da sua [classe de módulo](../../Module-Development-Basics.md), como no exemplo a seguir: + +````csharp +Configure<DynamicJavaScriptProxyOptions>(options => +{ + options.DisableModule("app"); +}); +```` + +`app` representa o aplicativo principal neste exemplo, o que funciona se você estiver criando um aplicativo. Se você estiver desenvolvendo um módulo de aplicativo, use o nome do seu módulo. + +## Detalhes do AJAX + +As funções de proxy do cliente JavaScript usam o [abp.ajax](JavaScript-API/Ajax.md) por baixo dos panos. Portanto, você tem os mesmos benefícios, como **tratamento automático de erros**. Além disso, você pode controlar totalmente a chamada AJAX fornecendo as opções. + +### O Valor de Retorno + +Cada função retorna um [objeto Deferred](https://api.jquery.com/category/deferred-object/). Isso significa que você pode encadear com `then` para obter o resultado, `catch` para lidar com o erro, `always` para executar uma ação assim que a operação for concluída (sucesso ou falha). + +### Opções do AJAX + +Cada função recebe um **último parâmetro** adicional após seus próprios parâmetros. O último parâmetro é chamado de `ajaxParams`. É um objeto que substitui as opções do AJAX. + +**Exemplo: Definir as opções do AJAX `type` e `dataType`** + +````js +acme.bookStore.authors.author + .delete('7245a066-5457-4941-8aa7-3004778775f0', { + type: 'POST', + dataType: 'xml' + }) + .then(function() { + abp.notify.info('Excluído com sucesso!'); + }); +```` + +Consulte a documentação do [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) para todas as opções disponíveis. + +## Veja Também + +* [Proxies de Cliente JavaScript Dinâmicos da API](Dynamic-JavaScript-Proxies.md) +* [Controladores de API Automáticos](../../API/Auto-API-Controllers.md) +* [Tutorial de Desenvolvimento de Aplicativos da Web](../../Tutorials/Part-1.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Alerts.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Alerts.md new file mode 100644 index 0000000000..636214cd3b --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Alerts.md @@ -0,0 +1,82 @@ +# Alertas + +## Introdução + +`abp-alert` é um elemento principal para criar um alerta. + +Uso básico: + +````xml +<abp-alert alert-type="Primary"> + Um alerta primário simples - confira! +</abp-alert> +```` + + + +## Demonstração + +Veja a página de demonstração de [alertas](https://bootstrap-taghelpers.abp.io/Components/Alerts) para vê-lo em ação. + +## Atributos + +### alert-type + +Um valor que indica o tipo de alerta. Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `Primary` +* `Secondary` +* `Success` +* `Danger` +* `Warning` +* `Info` +* `Light` +* `Dark` + +Exemplo: + +````xml +<abp-alert alert-type="Warning"> + Um alerta de aviso simples - confira! +</abp-alert> +```` + +### alert-link + +Um valor que fornece links coloridos correspondentes dentro de qualquer alerta. + +Exemplo: + +````xml +<abp-alert alert-type="Danger"> + Um alerta de perigo simples com <a abp-alert-link href="#">um exemplo de link</a>. Clique nele se quiser. +</abp-alert> +```` + +### dismissible + +Um valor para tornar o alerta dispensável. + +Exemplo: + +````xml +<abp-alert alert-type="Warning" dismissible="true"> + Santo guacamole! Você deve verificar alguns desses campos abaixo. +</abp-alert> +```` + +### Conteúdo adicional + +`abp-alert` também pode conter elementos HTML adicionais, como títulos, parágrafos e divisores. + +Exemplo: + +````xml +<abp-alert alert-type="Success"> + <h4>Bom trabalho!</h4> + <p>Aww yeah, você leu com sucesso esta importante mensagem de alerta. Este texto de exemplo vai ficar um pouco mais longo para que você possa ver como o espaçamento dentro de um alerta funciona com esse tipo de conteúdo.</p> + <hr> + <p class="mb-0">Sempre que precisar, certifique-se de usar utilitários de margem para manter as coisas organizadas.</p> +</abp-alert> +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Badges.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Badges.md new file mode 100644 index 0000000000..0c45b17821 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Badges.md @@ -0,0 +1,37 @@ +# Distintivos + +## Introdução + +`abp-badge` e `abp-badge-pill` são atributos de Tag Helper ABP para tags html `a` e `span`. + +Uso básico: + +````html +<span abp-badge="Primary">Primary</span> +<a abp-badge="Info" href="#">Info</a> +<a abp-badge-pill="Danger" href="#">Danger</a> +```` + +## Demonstração + +Veja a página de demonstração de [distintivos](https://bootstrap-taghelpers.abp.io/Components/Badges) para vê-los em ação. + +### Valores + +* Indica o tipo de distintivo. Deve ser um dos seguintes valores: + + * `Default` + * `Primary` + * `Secondary` + * `Success` + * `Danger` + * `Warning` + * `Info` + * `Light` + * `Dark` + +Exemplo: + +````html +<span abp-badge-pill="Danger">Danger</span> +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Blockquote.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Blockquote.md new file mode 100644 index 0000000000..cf3867d83d --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Blockquote.md @@ -0,0 +1,18 @@ +# Blockquote + +`abp-blockquote` é o contêiner principal para os itens de blockquote. + +Usos básicos: + +````html +<abp-blockquote> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> +</abp-blockquote> + +<abp-blockquote> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> + <footer>Alguém famoso em Título da Fonte</footer> +</abp-blockquote> +```` + +Ele adiciona a classe `blockquote` ao contêiner principal, também adiciona a classe `blockquote-footer` ao elemento `footer` interno e a classe `mb-0` ao elemento `p` interno. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Borders.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Borders.md new file mode 100644 index 0000000000..1f0aa64993 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Borders.md @@ -0,0 +1,126 @@ +# Bordas + +## Introdução + +`abp-border` é um atributo do ABP Tag Helper para estilização de bordas. + +Uso básico: + +````html +<span abp-border="Default"></span> +<span abp-border="Top"></span> +<span abp-border="Right"></span> +<span abp-border="Bottom"></span> +<span abp-border="Left"></span> +```` + +## Demonstração + +Veja a página de demonstração de [bordas](https://bootstrap-taghelpers.abp.io/Components/Borders) para vê-lo em ação. + +## Valores + +Um valor indica o tipo, posição e a cor da borda. Deve ser um dos seguintes valores: + +* `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` + +(Valores com `_0` no final são para usos [Subtrativos](https://getbootstrap.com/docs/4.0/utilities/borders/#subtractive)) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Breadcrumbs.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Breadcrumbs.md new file mode 100644 index 0000000000..0efd4469f8 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Breadcrumbs.md @@ -0,0 +1,25 @@ +# Trilha de navegação + +## Introdução + +`abp-breadcrumb` é o contêiner principal para os itens da trilha de navegação. + +Uso básico: + +````html +<abp-breadcrumb> + <abp-breadcrumb-item href="#" title="Início" /> + <abp-breadcrumb-item href="#" title="Biblioteca"/> + <abp-breadcrumb-item title="Página"/> +</abp-breadcrumb> +```` + +## Demonstração + +Veja a página de demonstração de [trilhas de navegação](https://bootstrap-taghelpers.abp.io/Components/Breadcrumbs) para vê-la em ação. + +## Atributos do abp-breadcrumb-item + +- **title**: Define o texto do item da trilha de navegação. +- **active**: Define o item da trilha de navegação ativo. O último item é ativo por padrão, se nenhum outro item estiver ativo. +- **href**: Um valor que indica se um `abp-breadcrumb-item` possui um link. Deve ser um valor de link em formato de string. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Button-groups.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Button-groups.md new file mode 100644 index 0000000000..ad751e4ff1 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Button-groups.md @@ -0,0 +1,37 @@ +# Grupos de botões + +## Introdução + +`abp-button-group` é o contêiner principal para elementos de botão agrupados. + +Uso básico: + +````html +<abp-button-group> + <abp-button button-type="Secondary">Esquerda</abp-button> + <abp-button button-type="Secondary">Meio</abp-button> + <abp-button button-type="Secondary">Direita</abp-button> +</abp-button-group> +```` + +## Demonstração + +Veja a página de demonstração de [grupos de botões](https://bootstrap-taghelpers.abp.io/Components/Button-groups) para vê-lo em ação. + +## Atributos + +### direction + +Um valor que indica a direção dos botões. Deve ser um dos seguintes valores: + +* `Horizontal` (valor padrão) +* `Vertical` + +### size + +Um valor que indica o tamanho dos botões no grupo. Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `Small` +* `Medium` +* `Large` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Buttons.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Buttons.md new file mode 100644 index 0000000000..6b5a4f01bd --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Buttons.md @@ -0,0 +1,98 @@ +# Botões + +## Introdução + +`abp-button` é o elemento principal para criar botões. + +Uso básico: + +````xml +<abp-button button-type="Primary">Clique em Mim</abp-button> +```` + +## Demonstração + +Veja a página de demonstração de [botões](https://bootstrap-taghelpers.abp.io/Components/Buttons) para vê-lo em ação. + +## Atributos + +### button-type + +Um valor que indica o estilo/tipo principal do botão. Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `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` + +### size + +Um valor que indica o tamanho do botão. Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `Small` +* `Medium` +* `Large` +* `Block` +* `Block_Small` +* `Block_Medium` +* `Block_Large` + +### busy-text + +Um texto que é mostrado quando o botão está ocupado. + +Para tornar o botão ocupado: + +````xml +$('#btnTest').buttonBusy(true); +```` + +Para torná-lo utilizável novamente: + +````xml +$('#btnTest').buttonBusy(false); +```` + +### text + +O texto do botão. Isso é um atalho se você simplesmente deseja definir um texto para o botão. Exemplo: + +````xml +<abp-button button-type="Primary" text="Clique em Mim" /> +```` + +Nesse caso, você pode usar uma tag de auto-fechamento para torná-lo mais curto. + +### icon + +Usado para definir um ícone para o botão. Funciona com as classes de ícone do [Font Awesome](https://fontawesome.com/) por padrão. Exemplo: + +````xml +<abp-button icon="address-card" text="Endereço" /> +```` + +##### icon-type + +Se você não deseja usar o font-awesome, você tem duas opções: + +1. Defina `icon-type` como `Other` e escreva a classe CSS do ícone de fonte que você está usando. +2. Se você não usa um ícone de fonte, use as tags de abertura e fechamento manualmente e escreva qualquer código dentro das tags. + +### disabled + +Defina `true` para desabilitar o botão inicialmente. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Cards.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Cards.md new file mode 100644 index 0000000000..136bb80809 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Cards.md @@ -0,0 +1,188 @@ +# Cartões + +## Introdução + +`abp-card` é um contêiner de conteúdo derivado do elemento de cartão do Bootstrap. + +Uso básico: + +````xml +<abp-card style="width: 18rem;"> + <img abp-card-image="Top" src="~/imgs/demo/300x200.png"/> + <abp-card-body> + <abp-card-title>Título do Cartão</abp-card-title> + <abp-card-text>Algum texto de exemplo rápido para construir o título do cartão e compor a maior parte do conteúdo do cartão.</abp-card-text> + <a abp-button="Primary" href="#">Ir para algum lugar</a> + </abp-card-body> +</abp-card> +```` + + + +##### Usando Títulos, Texto e Links: + +As seguintes tags podem ser usadas sob a tag principal `abp-card` + +* `abp-card-title` +* `abp-card-subtitle` +* `a abp-card-link` + +Exemplo: + +````xml +<abp-card style="width: 18rem;"> + <abp-card-body> + <abp-card-title>Título do cartão</abp-card-title> + <abp-card-subtitle class="mb-2 text-muted">Subtítulo do cartão</abp-card-subtitle> + <abp-card-text>Algum texto de exemplo rápido para construir o título do cartão e compor a maior parte do conteúdo do cartão.</abp-card-text> + <a abp-card-link href="#">Link do cartão</a> + <a abp-card-link href="#">Outro link</a> + </abp-card-body> +</abp-card> +```` + + + +##### Usando Grupos de Listas: + +* `abp-list-group flush="true"` : O atributo `flush` renderiza a classe `list-group-flush` do Bootstrap, que é usada para remover bordas e cantos arredondados para renderizar os itens do grupo de listas de ponta a ponta em um contêiner pai. +* `abp-list-group-item` + +Exemplo completo: + +````xml +<abp-card style="width: 18rem;"> + <img abp-card-image="Top" src="~/imgs/demo/300x200.png" /> + <abp-card-body> + <abp-card-title>Título do Cartão</abp-card-title> + <abp-card-text>Algum texto de exemplo rápido para construir o título do cartão e compor a maior parte do conteúdo do cartão.</abp-card-text> + </abp-card-body> + <abp-list-group flush="true"> + <abp-list-group-item>Cras justo odio</abp-list-group-item> + <abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item> + <abp-list-group-item>Vestibulum at eros</abp-list-group-item> + </abp-list-group> + <abp-card-body> + <a abp-card-link href="#">Link do cartão</a> + <a abp-card-link href="#">Outro link</a> + </abp-card-body> +</abp-card> +```` + + + +##### Usando Cabeçalho, Rodapé e Citação: + +* `abp-card-header` +* `abp-card-footer` +* `abp-blockquote` + +Exemplo: + +```xml +<abp-card style="width: 18rem;"> + <abp-card-header>Destaque</abp-card-header> + <abp-card-body> + <abp-card-title>Tratamento especial de título</abp-card-title> + <abp-card-text>Com texto de suporte abaixo como uma introdução natural a conteúdo adicional.</abp-card-text> + <a abp-button="Primary" href="#">Ir para algum lugar</a> + </abp-card-body> +</abp-card> +``` + +Exemplo de citação: + +```xml +<abp-card> + <abp-card-header>Citação</abp-card-header> + <abp-card-body> +<abp-blockquote> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> + <footer>Alguém famoso em Título da Fonte</footer> +</abp-blockquote> + </abp-card-body> +</abp-card> +``` + +Exemplo de rodapé: + +```xml +<abp-card class="text-center"> + <abp-card-header>Destaque</abp-card-header> + <abp-card-body> + <abp-blockquote> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> + <footer>Alguém famoso em Título da Fonte</footer> + </abp-blockquote> + </abp-card-body> + <abp-card-footer class="text-muted">2 dias atrás</abp-card-footer> +</abp-card> +``` + + + +## Demonstração + +Veja a [página de demonstração de cartões](https://bootstrap-taghelpers.abp.io/Components/Cards) para vê-lo em ação. + +## Atributos do abp-card + +- **background:** Um valor que indica a cor de fundo do cartão. +- **text-color**: Um valor que indica a cor do texto dentro do cartão. +- **border:** Um valor que indica a cor da borda dentro do cartão. + +Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `Primary` +* `Secondary` +* `Success` +* `Danger` +* `Warning` +* `Info` +* `Light` +* `Dark` + +Exemplo: + +````xml +<abp-card background="Success" text-color="Danger" border="Dark"> +```` + +### dimensionamento + +Os cartões têm largura padrão de 100% e podem ser alterados com CSS personalizado, classes de grade, mixins de grade Sass ou [utilitários](https://getbootstrap.com/docs/4.0/utilities/sizing/). + +````xml +<abp-card style="width: 18rem;"> +```` + +### card-deck e card-columns + +`abp-card` também pode ser usado dentro de `card-deck` ou `card-columns`. + +````xml +<div class="card-deck"> + <abp-card background="Primary"> + <abp-card-header>Primeiro Deck</abp-card-header> + <abp-card-body> + <abp-card-title>Ás</abp-card-title> + <abp-card-text>Aqui está o conteúdo para Ás.</abp-card-text> + </abp-card-body> + </abp-card> + <abp-card background="Info"> + <abp-card-header>Segundo Deck</abp-card-header> + <abp-card-body> + <abp-card-title>Beta</abp-card-title> + <abp-card-text>Conteúdo Beta.</abp-card-text> + </abp-card-body> + </abp-card> + <abp-card background="Warning"> + <abp-card-header>Terceiro Deck</abp-card-header> + <abp-card-body> + <abp-card-title>Epsilon</abp-card-title> + <abp-card-text>Conteúdo para Epsilon.</abp-card-text> + </abp-card-body> + </abp-card> +</div> +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Carousel.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Carousel.md new file mode 100644 index 0000000000..836e26e781 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Carousel.md @@ -0,0 +1,71 @@ +# Carrossel + +## Introdução + +`abp-carousel` é a tag abp para o elemento carrossel. + +Uso básico: + +````html +<abp-carousel> + <abp-carousel-item src=""></abp-carousel-item> + <abp-carousel-item src=""></abp-carousel-item> + <abp-carousel-item src=""></abp-carousel-item> +</abp-carousel> +```` + +## Demonstração + +Veja a página [carousel_demo](https://bootstrap-taghelpers.abp.io/Components/Carousel) para vê-lo em ação. + +## Atributos + +### id + +Um valor que define o id do carrossel. Se não for definido, um id gerado será definido quando a tag for criada. + +### controls + +Um valor para habilitar os controles (botões anterior e próximo) no carrossel. Deve ser um dos seguintes valores: + +* `false` +* `true` + +### indicators + +Um valor para habilitar os indicadores no carrossel. Deve ser um dos seguintes valores: + +* `false` +* `true` + +### crossfade + +Um valor para habilitar a animação de fade em vez de slide no carrossel. Deve ser um dos seguintes valores: + +* `false` +* `true` + +## Atributos do abp-carousel-item + +### caption-title + +Um valor que define o título da legenda do item do carrossel. + +### caption + +Um valor que define a legenda do item do carrossel. + +### src + +Um valor de link que define a origem da imagem exibida no item do carrossel. + +### active + +Um valor para definir o item ativo do carrossel. Deve ser um dos seguintes valores: + +* `false` +* `true` + +### alt + +Um valor que define o texto alternativo para a imagem do item do carrossel quando a imagem não pode ser exibida. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Collapse.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Collapse.md new file mode 100644 index 0000000000..1f1c88287f --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Collapse.md @@ -0,0 +1,92 @@ +# Colapso + +## Introdução + +`abp-collapse-body` é o contêiner principal para mostrar e ocultar conteúdo. `abp-collapse-id` é usado para mostrar e ocultar o contêiner de conteúdo. Pode ser acionado tanto com `abp-button` quanto com tags `a`. + +Uso básico: + +````html +<abp-button button-type="Primary" abp-collapse-id="collapseExample" text="Botão com data-target" /> +<a abp-button="Primary" abp-collapse-id="collapseExample"> Link com href </a> + +<abp-collapse-body id="collapseExample"> + Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. +</abp-collapse-body> +```` + + + +## Demonstração + +Veja a [página de demonstração de colapso](https://bootstrap-taghelpers.abp.io/Components/Collapse) para vê-lo em ação. + +## Atributos + +### show + +Um valor que indica se o corpo do colapso será inicializado visível ou oculto. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### multi + +Um valor que indica se um `abp-collapse-body` pode ser mostrado ou ocultado por um elemento que pode mostrar/ocultar vários corpos de colapso. Basicamente, esse atributo adiciona a classe "multi-collapse" a `abp-collapse-body`. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +Exemplo: + +````xml +<a abp-button="Primary" abp-collapse-id="FirstCollapseExample"> Alternar primeiro elemento </a> +<abp-button button-type="Primary" abp-collapse-id="SecondCollapseExample" text="Alternar segundo elemento" /> +<abp-button button-type="Primary" abp-collapse-id="FirstCollapseExample SecondCollapseExample" text="Alternar ambos os elementos" /> + +<abp-row class="mt-3"> + <abp-column size-sm="_6"> + <abp-collapse-body id="FirstCollapseExample" multi="true"> + Curabitur porta porttitor libero eu luctus. Praesent ultrices mattis commodo. Integer sodales massa risus, in molestie enim sagittis blandit + </abp-collapse-body> + </abp-column> + <abp-column size-sm="_6"> + <abp-collapse-body id="SecondCollapseExample" multi="true"> + Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. + </abp-collapse-body> + </abp-column> +</abp-row> +```` + +## Exemplo de acordeão + +`abp-accordion` é o contêiner principal para os itens do acordeão. + +Uso básico: + +````xml +<abp-accordion> + <abp-accordion-item title="Item do Grupo Colapsável #1"> + Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry rtat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. + </abp-accordion-item> + <abp-accordion-item title="Item do Grupo Colapsável #2"> + Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. + </abp-accordion-item> + <abp-accordion-item title="Item do Grupo Colapsável #3"> + Anim pariatur wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. + </abp-accordion-item> +</abp-accordion> +```` + +## Atributos + +### active + +Um valor que indica se o item do acordeão será inicializado visível ou oculto. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### title + +Um valor que indica o título visível do item do acordeão. Deve ser um valor de string. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Dropdowns.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Dropdowns.md new file mode 100644 index 0000000000..72441547bd --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Dropdowns.md @@ -0,0 +1,93 @@ +# Dropdowns + +## Introdução + +`abp-dropdown` é o contêiner principal para o conteúdo do dropdown. + +Uso básico: + +````xml +<abp-dropdown> + <abp-dropdown-button text="Botão do dropdown" /> + <abp-dropdown-menu> + <abp-dropdown-item href="#">Ação</abp-dropdown-item> + <abp-dropdown-item href="#">Outra ação</abp-dropdown-item> + <abp-dropdown-item href="#">Algo mais aqui</abp-dropdown-item> + </abp-dropdown-menu> +</abp-dropdown> +```` + +## Demonstração + +Veja a [página de demonstração do dropdown](https://bootstrap-taghelpers.abp.io/Components/Dropdowns) para vê-lo em ação. + +## Atributos + +### direction + +Um valor que indica em qual direção os botões do dropdown serão exibidos. Deve ser um dos seguintes valores: + +* `Down` (valor padrão) +* `Up` +* `Right` +* `Left` + +### dropdown-style + +Um valor que indica se um `abp-dropdown-button` terá um ícone dividido para o dropdown. Deve ser um dos seguintes valores: + +* `Single` (valor padrão) +* `Split` + +## Itens do menu + +`abp-dropdown-menu` é o contêiner principal para os itens do menu dropdown. + +Uso básico: + +````xml +<abp-dropdown> + <abp-dropdown-button button-type="Secondary" text="Dropdown"/> + <abp-dropdown-menu> + <abp-dropdown-header>Cabeçalho do Dropdown</abp-dropdown-header> + <abp-dropdown-item href="#">Ação</abp-dropdown-item> + <abp-dropdown-item active="true" href="#">Ação ativa</abp-dropdown-item> + <abp-dropdown-item disabled="true" href="#">Ação desativada</abp-dropdown-item> + <abp-dropdown-divider/> + <abp-dropdown-item-text>Texto do item do dropdown</abp-dropdown-item-text> + <abp-dropdown-item href="#">Algo mais aqui</abp-dropdown-item> + </abp-dropdown-menu> +</abp-dropdown> +```` + +## Atributos + +### align + +Um valor que indica em qual direção os itens do `abp-dropdown-menu` serão alinhados. Deve ser um dos seguintes valores: + +* `Start` (valor padrão) +* `End` + +### Conteúdo adicional + +`abp-dropdown-menu` também pode conter elementos HTML adicionais como títulos, parágrafos, divisores ou elementos de formulário. + +Exemplo: + +````xml +<abp-dropdown > + <abp-dropdown-button button-type="Secondary" text="Dropdown com Formulário"/> + <abp-dropdown-menu> + <form class="px-4 py-3"> + <abp-input asp-for="EmailAddress"></abp-input> + <abp-input asp-for="Password"></abp-input> + <abp-input asp-for="RememberMe"></abp-input> + <abp-button button-type="Primary" text="Entrar" type="submit" /> + </form> + <abp-dropdown-divider></abp-dropdown-divider> + <abp-dropdown-item href="#">Novo por aqui? Cadastre-se</abp-dropdown-item> + <abp-dropdown-item href="#">Esqueceu a senha?</abp-dropdown-item> + </abp-dropdown-menu> +</abp-dropdown> +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md new file mode 100644 index 0000000000..90a20812f2 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md @@ -0,0 +1,281 @@ +# Formulários Dinâmicos + +## Introdução + +`abp-dynamic-form` cria um formulário bootstrap para um modelo C# fornecido. + +Uso básico: + +````xml +<abp-dynamic-form abp-model="@Model.MyDetailedModel"/> +```` + +Modelo: + +````csharp +public class DynamicFormsModel : PageModel +{ + [BindProperty] + public DetailedModel MyDetailedModel { get; set; } + + public List<SelectListItem> CountryList { get; set; } = new List<SelectListItem> + { + new SelectListItem { Value = "CA", Text = "Canadá"}, + new SelectListItem { Value = "US", Text = "EUA"}, + new SelectListItem { Value = "UK", Text = "Reino Unido"}, + new SelectListItem { Value = "RU", Text = "Rússia"} + }; + + public void OnGet() + { + MyDetailedModel = new DetailedModel + { + Name = "", + Description = "Lorem ipsum dolor sit amet.", + IsActive = true, + Age = 65, + Day = DateTime.Now, + MyCarType = CarType.Coupe, + YourCarType = CarType.Sedan, + Country = "RU", + NeighborCountries = new List<string>() { "UK", "CA" } + }; + } + + public class DetailedModel + { + [Required] + [Placeholder("Digite seu nome...")] + [Display(Name = "Nome")] + public string Name { get; set; } + + [TextArea(Rows = 4)] + [Display(Name = "Descrição")] + [InputInfoText("Descreva-se")] + public string Description { get; set; } + + [Required] + [DataType(DataType.Password)] + [Display(Name = "Senha")] + public string Password { get; set; } + + [Display(Name = "Ativo")] + public bool IsActive { get; set; } + + [Required] + [Display(Name = "Idade")] + public int Age { get; set; } + + [Required] + [Display(Name = "Meu Tipo de Carro")] + public CarType MyCarType { get; set; } + + [Required] + [AbpRadioButton(Inline = true)] + [Display(Name = "Seu Tipo de Carro")] + public CarType YourCarType { get; set; } + + [DataType(DataType.Date)] + [Display(Name = "Dia")] + public DateTime Day { get; set; } + + [SelectItems(nameof(CountryList))] + [Display(Name = "País")] + public string Country { get; set; } + + [SelectItems(nameof(CountryList))] + [Display(Name = "Países Vizinhos")] + public List<string> NeighborCountries { get; set; } + } + + public enum CarType + { + Sedan, + Hatchback, + StationWagon, + Coupe + } +} +```` + +## Demonstração + +Veja a [página de demonstração de formulários dinâmicos](https://bootstrap-taghelpers.abp.io/Components/DynamicForms) para vê-lo em ação. + +## Atributos + +### abp-model + +Define o modelo C# para o formulário dinâmico. As propriedades deste modelo são convertidas em campos de entrada no formulário. + +### column-size + +Aqui, use 'col-sm' para definir o tamanho. Ao definir essa propriedade, 'col-12' será adicionado ao mesmo tempo. + +### submit-button + +Pode ser `True` ou `False`. + +Se `True`, um botão de envio será gerado na parte inferior do formulário. + +O valor padrão é `False`. + +### required-symbols + +Pode ser `True` ou `False`. + +Se `True`, as entradas obrigatórias terão um símbolo (*) que indica que são obrigatórias. + +O valor padrão é `True`. + +## Posicionamento do Conteúdo do Formulário + +Por padrão, `abp-dynamic-form` limpa o HTML interno e coloca os campos de entrada nele. Se você quiser adicionar conteúdo adicional ao formulário dinâmico ou colocar os campos de entrada em uma área específica, você pode usar a tag `<abp-form-content />`. Essa tag será substituída pelo conteúdo do formulário e o restante do HTML interno da tag `abp-dynamic-form` permanecerá inalterado. + +Uso: + +````xml +<abp-dynamic-form abp-model="@Model.MyExampleModel"> + <div> + Algum conteúdo.... + </div> + <div class="input-area"> + <abp-form-content /> + </div> + <div> + Mais algum conteúdo.... + </div> +</abp-dynamic-form> +```` + +## Ordem de Entrada + +`abp-dynamic-form` ordena as propriedades pelo atributo `DisplayOrder` e, em seguida, pela ordem das propriedades na classe do modelo. + +O número padrão do atributo `DisplayOrder` é 10000 para todas as propriedades. + +Veja o exemplo abaixo: + +````csharp +public class OrderExampleModel +{ + [DisplayOrder(10004)] + public string Name{ get; set; } + + [DisplayOrder(10005)] + public string Surname{ get; set; } + + //Padrão 10000 + public string EmailAddress { get; set; } + + [DisplayOrder(10003)] + public string PhoneNumber { get; set; } + + [DisplayOrder(9999)] + public string City { get; set; } +} +```` + +Neste exemplo, os campos de entrada serão exibidos com a seguinte ordem: `City` > `EmailAddress` > `PhoneNumber` > `Name` > `Surname`. + +## Ignorando uma propriedade + +Por padrão, `abp-dynamic-form` gera uma entrada para cada propriedade na classe do modelo. Se você quiser ignorar uma propriedade, use o atributo `DynamicFormIgnore`. + +Veja o exemplo abaixo: + +````csharp + public class MyModel + { + public string Name { get; set; } + + [DynamicFormIgnore] + public string Surname { get; set; } + } +```` + +Neste exemplo, nenhuma entrada será gerada para a propriedade `Surname`. + +## Indicando Caixa de Texto, Grupo de Rádio e Combobox + +Se você leu o documento [Elementos de Formulário](Form-elements.md), você percebeu que as tags `abp-radio` e `abp-select` são muito semelhantes no modelo C#. Portanto, temos que usar o atributo `[AbpRadioButton()]` para informar ao `abp-dynamic-form` qual das suas propriedades será um grupo de rádio e qual será uma combobox. Veja o exemplo abaixo: + +````xml +<abp-dynamic-form abp-model="@Model.MyDetailedModel"/> +```` + +Modelo: + +````csharp +public class DynamicFormsModel : PageModel +{ + [BindProperty] + public DetailedModel MyDetailedModel { get; set; } + + public List<SelectListItem> CountryList { get; set; } = new List<SelectListItem> + { + new SelectListItem { Value = "CA", Text = "Canadá"}, + new SelectListItem { Value = "US", Text = "EUA"}, + new SelectListItem { Value = "UK", Text = "Reino Unido"}, + new SelectListItem { Value = "RU", Text = "Rússia"} + }; + + public void OnGet() + { + MyDetailedModel = new DetailedModel + { + ComboCarType = CarType.Coupe, + RadioCarType = CarType.Sedan, + ComboCountry = "RU", + RadioCountry = "UK" + }; + } + + public class DetailedModel + { + public CarType ComboCarType { get; set; } + + [AbpRadioButton(Inline = true)] + public CarType RadioCarType { get; set; } + + [SelectItems(nameof(CountryList))] + public string ComboCountry { get; set; } + + [AbpRadioButton()] + [SelectItems(nameof(CountryList))] + public string RadioCountry { get; set; } + } + + public enum CarType + { + Sedan, + Hatchback, + StationWagon, + Coupe + } +} +```` + +Como você pode ver no exemplo acima: + +* Se `[AbpRadioButton()]` for usado em uma propriedade **Enum**, será um grupo de rádio. Caso contrário, será uma combobox. +* Se `[SelectItems()]` e `[AbpRadioButton()]` forem usados em uma propriedade, será um grupo de rádio. +* Se apenas `[SelectItems()]` for usado em uma propriedade, será uma combobox. +* Se nenhum desses atributos for usado em uma propriedade, será uma caixa de texto. + +## Localização + +`abp-dynamic-form` também lida com localização. + +Por padrão, ele tentará encontrar as chaves de localização "DisplayName:{PropertyName}" ou "{PropertyName}" e definir o valor de localização como rótulo de entrada. + +Você pode definir isso você mesmo usando o atributo `[Display()]` do Asp.Net Core. Você pode usar uma chave de localização neste atributo. Veja o exemplo abaixo: + +````csharp +[Display(Name = "Nome")] +public string Name { get; set; } +```` + +## Veja Também + +* [Elementos de Formulário](Form-elements.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Figure.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Figure.md new file mode 100644 index 0000000000..7e5721fe47 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Figure.md @@ -0,0 +1,14 @@ +# Figuras + +`abp-figure` é o contêiner principal para os itens de figura do bootstrap. + +Uso básico: + +````html +<abp-figure> + <abp-image src="..." class="img-fluid rounded" alt="Uma imagem de espaço reservado quadrado genérico com cantos arredondados em uma figura."> + <abp-figcaption class="text-end">Uma legenda para a imagem acima.</abp-figcaption> +</abp-figure> +```` + +Ele adiciona a classe `figure` ao contêiner principal, também adiciona a classe `figure-img` ao elemento `abp-image` interno e a classe `figure-caption` ao elemento `abp-figcaption` interno. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Form-elements.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Form-elements.md new file mode 100644 index 0000000000..c6bca8d3e7 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Form-elements.md @@ -0,0 +1,435 @@ +# Elementos de Formulário + +## Introdução + +O Abp fornece ajudantes de tag de entrada de formulário para facilitar a construção de formulários. + +## Demonstração + +Veja a página de demonstração dos [elementos de formulário](https://bootstrap-taghelpers.abp.io/Components/FormElements) para vê-los em ação. + +## abp-input + +A tag `abp-input` cria um campo de entrada de formulário do Bootstrap para uma propriedade C# específica. Ela usa o [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) em segundo plano, portanto, todos os atributos de anotação de dados da tag `input` do Asp.Net Core também são válidos para `abp-input`. + +Uso: + +````xml +<abp-input asp-for="@Model.MyModel.Name"/> +<abp-input asp-for="@Model.MyModel.Description"/> +<abp-input asp-for="@Model.MyModel.Password"/> +<abp-input asp-for="@Model.MyModel.IsActive"/> +```` + +Modelo: + +````csharp + public class FormElementsModel : PageModel + { + public SampleModel MyModel { get; set; } + + public void OnGet() + { + MyModel = new SampleModel(); + } + + public class SampleModel + { + [Required] + [Placeholder("Digite seu nome...")] + [InputInfoText("Qual é o seu nome?")] + public string Name { get; set; } + + [Required] + [FormControlSize(AbpFormControlSize.Large)] + public string SurName { get; set; } + + [TextArea(Rows = 4)] + public string Description { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + public bool IsActive { get; set; } + } + } +```` + +### Atributos + +Você pode definir alguns atributos na propriedade C# ou diretamente na tag HTML. Se você for usar essa propriedade em um [abp-dynamic-form](Dynamic-Forms.md), só poderá definir essas propriedades por meio de atributos de propriedade. + +#### Atributos de Propriedade + +- `[TextArea()]`: Converte a entrada em uma área de texto. + +* `[Placeholder()]`: Define a descrição da entrada. Você pode usar uma chave de localização diretamente. +* `[InputInfoText()]`: Define o texto para a entrada. Você pode usar uma chave de localização diretamente. +* `[FormControlSize()]`: Define o tamanho do elemento de wrapper form-control. Os valores disponíveis são: + - `AbpFormControlSize.Default` + - `AbpFormControlSize.Small` + - `AbpFormControlSize.Medium` + - `AbpFormControlSize.Large` +* `[DisabledInput]` : Define a entrada como desabilitada. +* `[ReadOnlyInput]`: Define a entrada como somente leitura. + +#### Atributos de Tag + +* `info`: Define o texto para a entrada. Você pode usar uma chave de localização diretamente. +* `auto-focus`: Permite que o navegador defina o foco no elemento quando o valor for verdadeiro. +* `size`: Define o tamanho do elemento de wrapper form-control. Os valores disponíveis são: + - `AbpFormControlSize.Default` + - `AbpFormControlSize.Small` + - `AbpFormControlSize.Medium` + - `AbpFormControlSize.Large` +* `disabled`: Define a entrada como desabilitada. +* `readonly`: Define a entrada como somente leitura. +* `label`: Define o rótulo da entrada. +* `required-symbol`: Adiciona o símbolo de obrigatório `(*)` ao rótulo quando a entrada é obrigatória. O valor padrão é `True`. +* `floating-label`: Define o rótulo como rótulo flutuante. O valor padrão é `False`. + +Os atributos `asp-format`, `name` e `value` do [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) também são válidos para o ajudante de tag `abp-input`. + +### Rótulo e Localização + +Você pode definir o rótulo da entrada de várias maneiras: + +- Você pode usar o atributo `Label` para definir o rótulo diretamente. Essa propriedade não localiza automaticamente o texto. Para localizar o rótulo, use `label="@L["{LocalizationKey}"].Value"`. +- Você pode defini-lo usando o atributo `[Display(name="{LocalizationKey}")]` do ASP.NET Core. +- Você pode deixar o **abp** encontrar a chave de localização para a propriedade. Ele tentará encontrar as chaves de localização "DisplayName:{PropertyName}" ou "{PropertyName}", se os atributos `label` ou `[DisplayName]` não estiverem definidos. + +## abp-select + +A tag `abp-select` cria um seletor de formulário do Bootstrap para uma propriedade C# específica. Ela usa o [ASP.NET Core Select Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper) em segundo plano, portanto, todos os atributos de anotação de dados da tag `select` do ASP.NET Core também são válidos para `abp-select`. + +A tag `abp-select` precisa de uma lista de `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem` para funcionar. Ela pode ser fornecida pelo atributo `asp-items` na tag ou pelo atributo `[SelectItems()]` na propriedade C# (se você estiver usando [abp-dynamic-form](Dynamic-Forms.md), o atributo C# é a única opção). + +O `abp-select` suporta seleção múltipla. + +O `abp-select` cria automaticamente uma lista de seleção para propriedades **Enum**. Nenhum dado extra é necessário. Se a propriedade for nula, uma chave e valor vazios serão adicionados no topo da lista gerada automaticamente. + +Uso: + +````xml +<abp-select asp-for="@Model.MyModel.City" asp-items="@Model.CityList"/> + +<abp-select asp-for="@Model.MyModel.AnotherCity"/> + +<abp-select asp-for="@Model.MyModel.MultipleCities" asp-items="@Model.CityList"/> + +<abp-select asp-for="@Model.MyModel.MyCarType"/> + +<abp-select asp-for="@Model.MyModel.MyNullableCarType"/> +```` + +Modelo: + +````csharp + public class FormElementsModel : PageModel + { + public SampleModel MyModel { get; set; } + + public List<SelectListItem> CityList { get; set; } + + public void OnGet() + { + MyModel = new SampleModel(); + + CityList = new List<SelectListItem> + { + new SelectListItem { Value = "NY", Text = "Nova York"}, + new SelectListItem { Value = "LDN", Text = "Londres"}, + new SelectListItem { Value = "IST", Text = "Istambul"}, + new SelectListItem { Value = "MOS", Text = "Moscou"} + }; + } + + public class SampleModel + { + public string City { get; set; } + + [SelectItems(nameof(CityList))] + public string AnotherCity { get; set; } + + public List<string> MultipleCities { get; set; } + + public CarType MyCarType { get; set; } + + public CarType? MyNullableCarType { get; set; } + } + + public enum CarType + { + Sedan, + Hatchback, + StationWagon, + Coupe + } + } +```` + +### Atributos + +Você pode definir alguns atributos na propriedade C# ou diretamente na tag HTML. Se você for usar essa propriedade em um [abp-dynamic-form](Dynamic-Forms.md), só poderá definir essas propriedades por meio de atributos de propriedade. + +#### Atributos de Propriedade + +* `[SelectItems()]`: Define os dados do seletor. O parâmetro deve ser o nome da lista de dados. (veja o exemplo acima) + +- `[InputInfoText()]`: Define o texto para a entrada. Você pode usar uma chave de localização diretamente. +- `[FormControlSize()]`: Define o tamanho do elemento de wrapper form-control. Os valores disponíveis são: + - `AbpFormControlSize.Default` + - `AbpFormControlSize.Small` + - `AbpFormControlSize.Medium` + - `AbpFormControlSize.Large` + +#### Atributos de Tag + +- `asp-items`: Define os dados do seletor. Isso deve ser uma lista de SelectListItem. +- `info`: Define o texto para a entrada. Você pode usar uma chave de localização diretamente. +- `size`: Define o tamanho do elemento de wrapper form-control. Os valores disponíveis são: + - `AbpFormControlSize.Default` + - `AbpFormControlSize.Small` + - `AbpFormControlSize.Medium` + - `AbpFormControlSize.Large` +- `label`: Define o rótulo da entrada. +- `required-symbol`: Adiciona o símbolo de obrigatório `(*)` ao rótulo quando a entrada é obrigatória. O valor padrão é `True`. + +### Rótulo e Localização + +Você pode definir o rótulo da entrada de várias maneiras: + +- Você pode usar o atributo `Label` e definir diretamente o rótulo. Mas isso não localiza automaticamente sua chave de localização. Portanto, use-o como `label="@L["{LocalizationKey}"].Value".` +- Você pode defini-lo usando o atributo `[Display(name="{LocalizationKey}")]` do ASP.NET Core. +- Você pode deixar o **abp** encontrar a chave de localização para a propriedade. Ele tentará encontrar as chaves de localização "DisplayName:{PropertyName}" ou "{PropertyName}". + +As localizações dos valores do combobox são definidas pelo `abp-select` para a propriedade **Enum**. Ele procura pelas chaves de localização "{EnumTypeName}.{EnumPropertyName}" ou "{EnumPropertyName}". Por exemplo, no exemplo acima, ele usará as chaves "CarType.StationWagon" ou "StationWagon" para localizar os valores do combobox. + +## abp-radio + +A tag `abp-radio` cria um grupo de rádio do Bootstrap para uma propriedade C# específica. O uso é muito semelhante à tag `abp-select`. + +Uso: + +````xml +<abp-radio asp-for="@Model.MyModel.CityRadio" asp-items="@Model.CityList" inline="true"/> + +<abp-radio asp-for="@Model.MyModel.CityRadio2"/> +```` + +Modelo: + +````csharp + public class FormElementsModel : PageModel + { + public SampleModel MyModel { get; set; } + + public List<SelectListItem> CityList { get; set; } = new List<SelectListItem> + { + new SelectListItem { Value = "NY", Text = "Nova York"}, + new SelectListItem { Value = "LDN", Text = "Londres"}, + new SelectListItem { Value = "IST", Text = "Istambul"}, + new SelectListItem { Value = "MOS", Text = "Moscou"} + }; + + public void OnGet() + { + MyModel = new SampleModel(); + MyModel.CityRadio = "IST"; + MyModel.CityRadio2 = "MOS"; + } + + public class SampleModel + { + public string CityRadio { get; set; } + + [SelectItems(nameof(CityList))] + public string CityRadio2 { get; set; } + } + } +```` + +### Atributos + +Você pode definir alguns atributos na propriedade C# ou diretamente na tag HTML. Se você for usar essa propriedade em um [abp-dynamic-form](Dynamic-Forms.md), só poderá definir essas propriedades por meio de atributos de propriedade. + +#### Atributos de Propriedade + +- `[SelectItems()]`: Define os dados do seletor. O parâmetro deve ser o nome da lista de dados. (veja o exemplo acima) + +#### Atributos de Tag + +- `asp-items`: Define os dados do seletor. Isso deve ser uma lista de SelectListItem. +- `Inline`: Se verdadeiro, os botões de rádio serão exibidos em uma única linha, um ao lado do outro. Se falso, eles serão exibidos um abaixo do outro. + +## abp-date-picker & abp-date-range-picker + +As tags `abp-date-picker` e `abp-date-range-picker` criam um seletor de data do Bootstrap para uma propriedade C# específica. O `abp-date-picker` é para seleção de uma única data, o `abp-date-range-picker` é para seleção de um intervalo de datas. Elas usam o plugin jQuery [datepicker](https://www.daterangepicker.com/). + +Uso: + +````xml +<abp-date-picker asp-for="@Model.MyModel.MyDate" /> +<abp-date-range-picker asp-for-start="@Model.MyModel.MyDateRangeStart" asp-for-end="@Model.MyModel.MyDateRangeEnd" /> +<abp-dynamic-form abp-model="DynamicFormExample"></abp-dynamic-form> +```` + +Modelo: + +````csharp + public class FormElementsModel : PageModel + { + public SampleModel MyModel { get; set; } + + public DynamicForm DynamicFormExample { get; set; } + + public void OnGet() + { + MyModel = new SampleModel(); + + DynamicFormExample = new DynamicForm(); + } + + public class SampleModel + { + public DateTime MyDate { get; set; } + + public DateTime MyDateRangeStart { get; set; } + + public DateTime MyDateRangeEnd { get; set; } + } + + public class DynamicForm + { + [DateRangePicker("MyPicker",true)] + public DateTime StartDate { get; set; } + + [DateRangePicker("MyPicker",false)] + [DatePickerOptions(nameof(DatePickerOptions))] + public DateTime EndDate { get; set; } + + public DateTime DateTime { get; set; } + + public DynamicForm() + { + StartDate = DateTime.Now; + EndDate = DateTime.Now; + DateTime = DateTime.Now; + } + } + + public AbpDatePickerOptions DatePickerOptions { get; set; } + } +```` + +### Atributos + +Você pode definir alguns atributos na propriedade C# ou diretamente na tag HTML. Se você for usar essa propriedade em um [abp-dynamic-form](Dynamic-Forms.md), só poderá definir essas propriedades por meio de atributos de propriedade. + +#### Atributos de Propriedade + +* `[Placeholder()]`: Define a descrição da entrada. Você pode usar uma chave de localização diretamente. +* `[InputInfoText()]`: Define o texto para a entrada. Você pode usar uma chave de localização diretamente. +* `[FormControlSize()]`: Define o tamanho do elemento de wrapper form-control. Os valores disponíveis são: + - `AbpFormControlSize.Default` + - `AbpFormControlSize.Small` + - `AbpFormControlSize.Medium` + - `AbpFormControlSize.Large` +* `[DisabledInput]` : Define a entrada como desabilitada. +* `[ReadOnlyInput]`: Define a entrada como somente leitura. +- `[DatePickerOptions()]`: Define as opções predefinidas do seletor de data. O parâmetro deve ser o nome da propriedade de opções (veja o exemplo acima). Veja as opções de [datepicker disponíveis](https://www.daterangepicker.com/#options). Você pode usar uma chave de localização diretamente. + +##### abp-date-picker +`[DatePicker]` : Define a entrada como seletor de data. Especialmente para propriedades de string. + +##### abp-date-range-picker +`[DateRangePicker()]` : Define o ID do seletor para o seletor de intervalo de datas. Você pode definir a propriedade como uma data de início definindo IsStart=true ou deixá-la como padrão/falso para definir como uma data de término. + +#### Atributos de Tag + +* `info`: Define o texto para a entrada. Você pode usar uma chave de localização diretamente. +* `auto-focus`: Permite que o navegador defina o foco no elemento quando o valor for verdadeiro. +* `size`: Define o tamanho do elemento de wrapper form-control. Os valores disponíveis são: + - `AbpFormControlSize.Default` + - `AbpFormControlSize.Small` + - `AbpFormControlSize.Medium` + - `AbpFormControlSize.Large` +* `disabled`: Define a entrada como desabilitada. +* `readonly`: Define a entrada como somente leitura. +* `label`: Define o rótulo da entrada. +* `required-symbol`: Adiciona o símbolo de obrigatório `(*)` ao rótulo quando a entrada é obrigatória. O valor padrão é `True`. +* `open-button`: Um botão para abrir o seletor de data será adicionado quando for `True`. O valor padrão é `True`. +* `clear-button`: Um botão para limpar o seletor de data será adicionado quando for `True`. O valor padrão é `True`. +* `single-open-and-clear-button`: Mostra os botões de abrir e limpar em um único botão quando for `True`. O valor padrão é `True`. +* `is-utc`: Converte a data para UTC quando for `True`. O valor padrão é `False`. +* `is-iso`: Converte a data para o formato ISO quando for `True`. O valor padrão é `False`. +* `visible-date-format`: Define o formato de data da entrada. O formato padrão é o formato de data da cultura do usuário. Você precisa fornecer uma convenção de formato de data JavaScript. Por exemplo: `YYYY-MM-DDTHH:MM:SSZ`. +* `input-date-format`: Define o formato de data do input oculto para compatibilidade com o backend. O formato padrão é `YYYY-MM-DD`. Você precisa fornecer uma convenção de formato de data JavaScript. Por exemplo: `YYYY-MM-DDTHH:MM:SSZ`. +* `date-separator`: Define um caractere para separar as datas de início e término. O valor padrão é `-` +* Outros atributos não mapeados serão adicionados automaticamente ao elemento de entrada como estão. Veja as opções de [datepicker disponíveis](https://www.daterangepicker.com/#options). Por exemplo: `data-start-date="2020-01-01"` + +##### abp-date-picker + +* `asp-date`: Define o valor da data. Isso deve ser um valor `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` ou `string`. + +##### abp-date-range-picker + +* `asp-for-start`: Define o valor da data de início. Isso deve ser um valor `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` ou `string`. +* `asp-for-end`: Define o valor da data de término. Isso deve ser um valor `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` ou `string`. + +### Rótulo e Localização + +Você pode definir o rótulo da entrada de várias maneiras: + +- Você pode usar o atributo `Label` para definir o rótulo diretamente. Essa propriedade não localiza automaticamente o texto. Para localizar o rótulo, use `label="@L["{LocalizationKey}"].Value"`. +- Você pode defini-lo usando o atributo `[Display(name="{LocalizationKey}")]` do ASP.NET Core. +- Você pode deixar o **abp** encontrar a chave de localização para a propriedade. Ele tentará encontrar as chaves de localização "DisplayName:{PropertyName}" ou "{PropertyName}". + +### Uso em JavaScript + +````javascript +var newPicker = abp.libs.bootstrapDateRangePicker.createDateRangePicker( + { + label: "Novo Seletor", + } +); +newPicker.insertAfter($('body')); +```` + +````javascript +var newPicker = abp.libs.bootstrapDateRangePicker.createSinglePicker( + { + label: "Novo Seletor", + } +); +newPicker.insertAfter($('body')); +```` + +#### Opções + +* `label`: Define o rótulo da entrada. +* `placeholder`: Define o espaço reservado da entrada. +* `value`: Define o valor da entrada. +* `name`: Define o nome da entrada. +* `id`: Define o ID da entrada. +* `required`: Define a entrada como obrigatória. +* `disabled`: Define a entrada como desabilitada. +* `readonly`: Define a entrada como somente leitura. +* `size`: Define o tamanho do elemento de wrapper form-control. Os valores disponíveis são: + - `AbpFormControlSize.Default` + - `AbpFormControlSize.Small` + - `AbpFormControlSize.Medium` + - `AbpFormControlSize.Large` +* `openButton`: Um botão para abrir o seletor de data será adicionado quando for `True`. O valor padrão é `True`. +* `clearButton`: Um botão para limpar o seletor de data será adicionado quando for `True`. O valor padrão é `True`. +* `singleOpenAndClearButton`: Mostra os botões de abrir e limpar em um único botão quando for `True`. O valor padrão é `True`. +* `isUtc`: Converte a data para UTC quando for `True`. O valor padrão é `False`. +* `isIso`: Converte a data para o formato ISO quando for `True`. O valor padrão é `False`. +* `visibleDateFormat`: Define o formato de data da entrada. O formato padrão é o formato de data da cultura do usuário. Você precisa fornecer uma convenção de formato de data JavaScript. Por exemplo: `YYYY-MM-DDTHH:MM:SSZ`. +* `inputDateFormat`: Define o formato de data do input oculto para compatibilidade com o backend. O formato padrão é `YYYY-MM-DD`. Você precisa fornecer uma convenção de formato de data JavaScript. Por exemplo: `YYYY-MM-DDTHH:MM:SSZ`. +* `dateSeparator`: Define um caractere para separar as datas de início e término. O valor padrão é `-`. +* `startDateName`: Define o nome do input de data de início oculto. +* `endDateName`: Define o nome do input de data de término oculto. +* `dateName`: Define o nome do input de data oculto. +* Outras [opções de datepicker](https://www.daterangepicker.com/#options). Por exemplo: `startDate: "2020-01-01"`. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Grids.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Grids.md new file mode 100644 index 0000000000..f540baa430 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Grids.md @@ -0,0 +1,273 @@ +# Grids + +## Introdução + +Abp tag helpers para o sistema de grid baseado no bootstrap. + +## Demonstração + +Veja a página de demonstração [grids demo page](https://bootstrap-taghelpers.abp.io/Components/Grids) para ver em ação. + +### Dimensionamento + +**Largura igual:** Cria colunas com largura igual. + +Exemplo: + +````xml +<abp-container> + <abp-row> + <abp-column abp-border="Info">1 de 2</abp-column> + <abp-column abp-border="Danger">2 de 2</abp-column> + </abp-row> + <abp-row> + <abp-column abp-border="Primary">1 de 3</abp-column> + <abp-column abp-border="Secondary">2 de 3</abp-column> + <abp-column abp-border="Dark">3 de 3</abp-column> + </abp-row> +</abp-container> +```` + +**Quebra de coluna:** `abp-column-breaker` é usado para quebrar a largura automática de colocação da linha atual e iniciar em uma nova linha em seguida. + +Exemplo: + +````xml +<abp-container> + <abp-row> + <abp-column>coluna</abp-column> + <abp-column>coluna</abp-column> + <abp-column-breaker/> + <abp-column>coluna</abp-column> + <abp-column>coluna</abp-column> + </abp-row> +</abp-container> +```` + +**Definindo a largura de uma coluna:** o atributo size é usado para definir a largura de uma coluna específica. + +Exemplo: + +```xml +<abp-container> + <abp-row> + <abp-column>1 de 3</abp-column> + <abp-column size="_6">2 de 3 (mais larga)</abp-column> + <abp-column>3 de 3</abp-column> + </abp-row> + <abp-row> + <abp-column>1 de 3</abp-column> + <abp-column size="_5">2 de 3 (mais larga)</abp-column> + <abp-column>3 de 3</abp-column> + </abp-row> +</abp-container> +``` + +**Conteúdo de largura variável:** Coluna de redimensionamento automático com base no conteúdo. + +```xml +<abp-container> + <abp-row h-align="Center"> + <abp-column size-lg="_2" abp-border="Info">1 de 3</abp-column> + <abp-column size-md="Auto" abp-border="Danger">Contrary to popular belief, Lorem Ipsum is not simply random text.</abp-column> + <abp-column size-lg="_2" abp-border="Warning">3 de 3</abp-column> + </abp-row> + <abp-row> + <abp-column>1 de 3</abp-column> + <abp-column size-md="Auto">Conteúdo de largura variável</abp-column> + <abp-column size-lg="_2">3 de 3</abp-column> + </abp-row> +</abp-container> +``` + +### Classes Responsivas + +As classes responsivas podem ser usadas de forma fortemente tipada dentro das tags abp. + +```xml +<abp-row> + <abp-column size-sm="_8">col-sm-8</abp-column> + <abp-column size-sm="_4">col-sm-4</abp-column> +</abp-row> +<abp-row> + <abp-column size-sm="_">col-sm</abp-column> + <abp-column size-sm="_">col-sm</abp-column> + <abp-column size-sm="_">col-sm</abp-column> + <abp-column size-sm="_">col-sm</abp-column> +</abp-row> +<!-- Empilhe as colunas em dispositivos móveis, tornando uma em largura total e a outra em meia largura --> +<abp-row> + <abp-column size="_12" size-md="_8">.col-12 .col-md-8</abp-column> + <abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column> +</abp-row> + +<!-- As colunas começam com 50% de largura em dispositivos móveis e aumentam para 33,3% de largura em desktop --> +<abp-row> + <abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column> + <abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column> + <abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column> +</abp-row> + +<!-- As colunas têm sempre 50% de largura, em dispositivos móveis e desktop --> +<abp-row> + <abp-column size="_6">.col-6</abp-column> + <abp-column size="_6">.col-6</abp-column> +</abp-row> +``` + +### Alinhamento + +Os alinhamentos de coluna podem ser feitos de forma fortemente tipada nas tags abp, tanto vertical quanto horizontalmente. + +**Alinhamento vertical**: o valor do atributo `v-align` é usado para alinhar as colunas verticalmente. + +Exemplo: + +```xml +<abp-container> + <abp-row v-align="Start"> + <abp-column>coluna</abp-column> + <abp-column>coluna</abp-column> + <abp-column>coluna</abp-column> + </abp-row> + <abp-row v-align="Center"> + <abp-column>coluna</abp-column> + <abp-column>coluna</abp-column> + <abp-column>coluna</abp-column> + </abp-row> + <abp-row v-align="End"> + <abp-column>coluna</abp-column> + <abp-column>coluna</abp-column> + <abp-column>coluna</abp-column> + </abp-row> +</abp-container> +``` + +**Alinhamento horizontal**: o valor do atributo `h-align` é usado para alinhar as colunas horizontalmente. + +Exemplo: + +```xml +<abp-container> + <abp-row h-align="Start"> + <abp-column size="_4">Uma de duas colunas</abp-column> + <abp-column size="_4">Uma de duas colunas</abp-column> + </abp-row> + <abp-row h-align="Center"> + <abp-column size="_4">Uma de duas colunas</abp-column> + <abp-column size="_4">Uma de duas colunas</abp-column> + </abp-row> + <abp-row h-align="End"> + <abp-column size="_4">Uma de duas colunas</abp-column> + <abp-column size="_4">Uma de duas colunas</abp-column> + </abp-row> + <abp-row h-align="Around"> + <abp-column size="_4">Uma de duas colunas</abp-column> + <abp-column size="_4">Uma de duas colunas</abp-column> + </abp-row> + <abp-row h-align="Between"> + <abp-column size="_4">Uma de duas colunas</abp-column> + <abp-column size="_4">Uma de duas colunas</abp-column> + </abp-row> +</abp-container> +``` + +**Sem margens**: as margens entre as colunas nas classes de grade predefinidas podem ser removidas com `gutters="false"`. Isso remove as margens negativas de `abp-row` e o preenchimento horizontal de todas as colunas filhas imediatas. + +Exemplo: + +```xml +<abp-row gutters="false"> + <abp-column size="_8">Uma de duas colunas</abp-column> + <abp-column size="_4">Uma de duas colunas</abp-column> +</abp-row> +``` + +**Quebra de coluna**: se mais de 12 colunas forem colocadas em uma única linha, cada grupo de colunas extras será, como uma unidade, quebrado em uma nova linha. + +Exemplo: + +```xml +<abp-row> + <abp-column size="_9">.col-9</abp-column> + <abp-column size="_4">.col-4<br>Since 9 + 4 = 13 > 12, this 4-column-wide div gets wrapped onto a new line as one contiguous unit.</abp-column> + <abp-column size="_6">.col-6<br>Subsequent columns continue along the new line.s</abp-column> +</abp-row> +``` + +### Reordenamento + +**Classes de ordem**: o atributo `order` é usado para controlar a ordem visual do conteúdo. + +Exemplo: + +```xml +<abp-container> + <abp-row> + <abp-column order="_12">Primeiro, mas último</abp-column> + <abp-column>Segundo, mas não ordenado</abp-column> + <abp-column order="_6">Terceiro, mas segundo</abp-column> + </abp-row> +</abp-container> +``` + +**Deslocamento de colunas**: o atributo `offset` é usado para definir o deslocamento das colunas da grade. + +Exemplo: + +```xml +<abp-container> + <abp-row> + <abp-column size-md="_4">.col-md-4</abp-column> + <abp-column size-md="_4" offset-md="_4">.col-md-4 .offset-md-4</abp-column> + </abp-row> + <abp-row> + <abp-column size-md="_3" offset-md="_3">.col-md-3 .offset-md-3</abp-column> + <abp-column size-md="_3" offset-md="_3">.col-md-3 .offset-md-3</abp-column> + </abp-row> + <abp-row> + <abp-column size-md="_6" offset-md="_3">.col-md-6 .offset-md-3</abp-column> + </abp-row> + <abp-row> + <abp-column size-sm="_5" size-md="_6">.col-sm-5 .col-md-6</abp-column> + <abp-column size-sm="_5" offset-sm="_2" size-md="_6" offset-md="_">.col-sm-5 .offset-sm-2 .col-md-6 .offset-md-0</abp-column> + </abp-row> + <abp-row> + <abp-column size-sm="_6" size-md="_5" size-lg="_6">col-sm-6 .col-md-5 .col-lg-6</abp-column> + <abp-column size-sm="_6" size-md="_5" offset-md="_2" size-lg="_6" offset-lg="_">.col-sm-6 .col-md-5 .offset-md-2 .col-lg-6 .offset-lg-0</abp-column> + </abp-row> +</abp-container> +``` + +## Atributos de abp-row + +- **v-align:** Um valor que indica o posicionamento vertical das colunas contidas. Deve ser um dos seguintes valores: + * `Default` (valor padrão) + * `Start` + * `Center` + * `End` + +- **h-align**: Um valor que indica o posicionamento horizontal das colunas contidas. Deve ser um dos seguintes valores: + * `Default` (valor padrão) + * `Start` + * `Center` + * `Around` + * `Between` + * `End` +- **gutter**: Um valor que indica se as margens negativas e o preenchimento horizontal serão removidos de todas as colunas filhas. Atuará como valor `true` se esse atributo não for definido. Deve ser um dos seguintes valores: + * `true` + * `false` + +## Atributos de abp-column + +- **size:** Um valor que indica a largura da coluna de `_`, `Undefined`, `_1`..`_12`, `Auto`. Ou pode ser usado com valores predefinidos como: + - `size-sm` + - `size-md` + - `size-lg` + - `size-xl` +- **order**: Um valor que indica a ordem da coluna de `Undefined`, `_1`..`_12`, `First` e `Last`. +- **offset:** Um valor que indica o deslocamento da coluna de `_`, `Undefined`, `_1`..`_12`, `Auto`. Ou pode ser usado com valores predefinidos como: + - `offset-sm` + - `offset-md` + - `offset-lg` + - `offset-xl` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Index.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Index.md new file mode 100644 index 0000000000..6187e98546 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Index.md @@ -0,0 +1,45 @@ +# ABP Tag Helpers + +O ABP Framework define um conjunto de **componentes de tag helper** para simplificar o desenvolvimento da interface do usuário para aplicativos ASP.NET Core (MVC / Razor Pages). + +## Wrappers de Componentes Bootstrap + +A maioria dos tag helpers são wrappers do [Bootstrap](https://getbootstrap.com/) (v5+). Codificar o Bootstrap não é tão fácil, não é tão seguro em termos de tipo e contém muitas tags HTML repetitivas. Os ABP Tag Helpers tornam isso mais **fácil** e **seguro em termos de tipo**. + +Não temos como objetivo envolver 100% dos componentes do Bootstrap. Ainda é possível escrever **código de estilo nativo do Bootstrap** (na verdade, os tag helpers geram código nativo do Bootstrap no final), mas sugerimos usar os tag helpers sempre que possível. + +O ABP Framework também adiciona alguns **recursos úteis** aos componentes padrão do Bootstrap. + +Aqui está a lista de componentes que são envolvidos pelo ABP Framework: + +* [Alertas](Alerts.md) +* [Badges](Badges.md) +* [Blockquote](Blockquote.md) +* [Bordas](Borders.md) +* [Breadcrumb](Breadcrumbs.md) +* [Botões](Buttons.md) +* [Cards](Cards.md) +* [Carousel](Carousel.md) +* [Collapse](Collapse.md) +* [Dropdowns](Dropdowns.md) +* [Figuras](Figure.md) +* [Grids](Grids.md) +* [List Groups](List-Groups.md) +* [Modals](Modals.md) +* [Navegação](Navs.md) +* [Paginator](Paginator.md) +* [Popovers](Popovers.md) +* [Barras de Progresso](Progress-Bars.md) +* [Tabelas](Tables.md) +* [Abas](Tabs.md) +* [Tooltips](Tooltips.md) + +> Até que todos os tag helpers sejam documentados, você pode visitar https://bootstrap-taghelpers.abp.io/ para vê-los com exemplos ao vivo. + +## Elementos de Formulário + +Os **Abp Tag Helpers** adicionam novos recursos aos **Tag Helpers de entrada e seleção do Asp.Net Core MVC** padrão e os envolvem com controles de formulário do **Bootstrap**. Consulte a documentação de [Elementos de Formulário](Form-elements.md). + +## Formulários Dinâmicos + +Os **Abp Tag Helpers** oferecem uma maneira fácil de construir formulários completos do **Bootstrap**. Consulte a documentação de [Formulários Dinâmicos](Dynamic-Forms.md). \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/List-Groups.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/List-Groups.md new file mode 100644 index 0000000000..78a37979da --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/List-Groups.md @@ -0,0 +1,76 @@ +# Listas de Grupos + +## Introdução + +`abp-list-group` é o contêiner principal para o conteúdo do grupo de listas. + +Uso básico: + +````xml +<abp-list-group> + <abp-list-group-item>Cras justo odio</abp-list-group-item> + <abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item> + <abp-list-group-item>Morbi leo risus</abp-list-group-item> + <abp-list-group-item>Vestibulum at eros</abp-list-group-item> +</abp-list-group> +```` + +## Demonstração + +Veja a [página de demonstração de grupos de listas](https://bootstrap-taghelpers.abp.io/Components/ListGroup) para vê-lo em ação. + +## Atributos + +### flush + +Um valor indica que os itens `abp-list-group` devem remover algumas bordas e cantos arredondados para renderizar os itens do grupo de listas de ponta a ponta em um contêiner pai. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### active + +Um valor indica se um `abp-list-group-item` deve estar ativo. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### disabled + +Um valor indica se um `abp-list-group-item` deve estar desativado. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### href + +Um valor indica se um `abp-list-group-item` possui um link. Deve ser um valor de link de string. + +### type + +Um valor indica uma classe de estilo `abp-list-group-item` com um plano de fundo e cor com estado. Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `Primary` +* `Secondary` +* `Success` +* `Danger` +* `Warning` +* `Info` +* `Light` +* `Dark` +* `Link` + +### Conteúdo adicional + +`abp-list-group-item` também pode conter elementos HTML adicionais, como spans. + +Exemplo: + +````xml +<abp-list-group> + <abp-list-group-item>Cras justo odio <span abp-badge-pill="Primary">14</span></abp-list-group-item> + <abp-list-group-item>Dapibus ac facilisis in <span abp-badge-pill="Primary">2</span></abp-list-group-item> + <abp-list-group-item>Morbi leo risus <span abp-badge-pill="Primary">1</span></abp-list-group-item> +</abp-list-group> +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Modals.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Modals.md new file mode 100644 index 0000000000..99fab4bbed --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Modals.md @@ -0,0 +1,88 @@ +# Modais + +> Este documento explica os detalhes do `abp-modal` Tag Helper, que simplifica a construção da marcação HTML para uma caixa de diálogo modal. Leia [essa documentação](../Modals.md) para aprender como trabalhar com modais. + +## Introdução + +`abp-modal` é um elemento principal para criar um modal. + +Uso básico: + +````xml +<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Abrir modal</abp-button> + +<abp-modal centered="true" scrollable="true" size="Large" id="myModal"> + <abp-modal-header title="Título do modal"></abp-modal-header> + <abp-modal-body> + Uau, você está lendo este texto em um modal! + </abp-modal-body> + <abp-modal-footer buttons="Fechar"></abp-modal-footer> +</abp-modal> +```` + +## Demonstração + +Veja a [página de demonstração de modais](https://bootstrap-taghelpers.abp.io/Components/Modals) para vê-lo em ação. + +## Atributos + +### centered + +Um valor que indica o posicionamento do modal. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### Scrollable + +Um valor que indica a rolagem do modal. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### size + +Um valor que indica o tamanho do modal. Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `Small` +* `Large` +* `ExtraLarge` + +### static + +Um valor que indica se o modal será estático. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### Conteúdo adicional + +`abp-modal-footer` pode ter vários botões com opção de alinhamento. + +Adicione `@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal` à sua página. + +Exemplo: + +````xml +<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Abrir modal</abp-button> + +<abp-modal centered="true" size="Large" id="myModal" static="true"> + <abp-modal-header title="Título do modal"></abp-modal-header> + <abp-modal-body> + Uau, você está lendo este texto em um modal! + </abp-modal-body> + <abp-modal-footer buttons="@(AbpModalButtons.Save|AbpModalButtons.Close)" button-alignment="Between"></abp-modal-footer> +</abp-modal> +```` + +### button-alignment + +Um valor que indica o posicionamento dos botões do rodapé do modal. Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `Start` +* `Center` +* `Around` +* `Between` +* `End` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Navs.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Navs.md new file mode 100644 index 0000000000..0ed0b806a2 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Navs.md @@ -0,0 +1,114 @@ +# Navs + +## Introdução + +`abp-nav` é o componente básico de tag helper derivado do elemento de navegação do bootstrap. + +Uso básico: + +````html +<abp-nav nav-style="Pill" align="Center"> + <abp-nav-item> +<a abp-nav-link active="true" href="#">Ativo</a> + </abp-nav-item> + <abp-nav-item> +<a abp-nav-link href="#">Link de navegação mais longo</a> + </abp-nav-item> + <abp-nav-item> +<a abp-nav-link href="#">link</a> + </abp-nav-item> + <abp-nav-item> +<a abp-nav-link disabled="true" href="#">desativado</a> + </abp-nav-item> +</abp-nav> +```` + +## Demonstração + +Veja a [página de demonstração de navegação](https://bootstrap-taghelpers.abp.io/Components/Navs) para vê-la em ação. + +## Atributos do abp-nav + +- **nav-style**: O valor indica o posicionamento e estilo dos itens contidos. Deve ser um dos seguintes valores: + * `Default` (valor padrão) + * `Vertical` + * `Pill` + * `PillVertical` +- **align:** O valor indica o alinhamento dos itens contidos: + * `Default` (valor padrão) + * `Start` + * `Center` + * `End` + +### Atributos do abp-nav-bar + +- **nav-style**: O valor indica o layout de cores da barra de navegação base. Deve ser um dos seguintes valores: + * `Default` (valor padrão) + * `Dark` + * `Light` + * `Dark_Primary` + * `Dark_Secondary` + * `Dark_Success` + * `Dark_Danger` + * `Dark_Warning` + * `Dark_Info` + * `Dark_Dark` + * `Dark_Link` + * `Light_Primary` + * `Light_Secondary` + * `Light_Success` + * `Light_Danger` + * `Light_Warning` + * `Light_Info` + * `Light_Dark` + * `Light_Link` +- **size:** O valor indica o tamanho da barra de navegação base. Deve ser um dos seguintes valores: + * `Default` (valor padrão) + * `Sm` + * `Md` + * `Lg` + * `Xl` + +### Atributos do abp-nav-item + +**dropdown**: Um valor que define o item de navegação como um menu suspenso, se fornecido. Pode ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +Exemplo: + +````html +<abp-nav-bar size="Lg" navbar-style="Dark_Warning"> + <a abp-navbar-brand href="#">Navbar</a> + <abp-navbar-toggle> + <abp-navbar-nav> + <abp-nav-item active="true"> + <a abp-nav-link href="#">Início <span class="sr-only">(atual)</span></a> + </abp-nav-item> + <abp-nav-item> + <a abp-nav-link href="#">Link</a> + </abp-nav-item> + <abp-nav-item dropdown="true"> + <abp-dropdown> + <abp-dropdown-button nav-link="true" text="Dropdown" /> + <abp-dropdown-menu> + <abp-dropdown-header>Cabeçalho do menu suspenso</abp-dropdown-header> + <abp-dropdown-item href="#" active="true">Ação</abp-dropdown-item> + <abp-dropdown-item href="#" disabled="true">Outra ação desativada</abp-dropdown-item> + <abp-dropdown-item href="#">Algo mais aqui</abp-dropdown-item> + <abp-dropdown-divider /> + <abp-dropdown-item href="#">Link separado</abp-dropdown-item> + </abp-dropdown-menu> + </abp-dropdown> + </abp-nav-item> + <abp-nav-item> + <a abp-nav-link disabled="true" href="#">Desativado</a> + </abp-nav-item> + </abp-navbar-nav> + <span abp-navbar-text> + Texto de exemplo + </span> + </abp-navbar-toggle> +</abp-nav-bar> +``` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Paginator.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Paginator.md new file mode 100644 index 0000000000..3cf94a002c --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Paginator.md @@ -0,0 +1,57 @@ +# Paginador + +## Introdução + +`abp-paginator` é a tag abp para paginação. Requer um modelo do tipo `Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel`. + +Uso básico: + +````xml +<abp-paginator model="Model.PagerModel" show-info="true"></abp-paginator> +```` + +Modelo: + +````csharp +using Microsoft.AspNetCore.Mvc.RazorPages; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components +{ + public class PaginatorModel : PageModel + { + public PagerModel PagerModel { get; set; } + + public void OnGet(int currentPage, string sort) + { + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); + } + } +} +```` + + + +## Demonstração + +Veja a [página de demonstração do paginador](https://bootstrap-taghelpers.abp.io/Components/Paginator) para vê-lo em ação. + +## Atributos + +### model + +O modelo do tipo `Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` pode ser inicializado com os seguintes dados: + +* `totalCount` +* `shownItemsCount` +* `currentPage` +* `pageSize` +* `pageUrl` +* `sort` (padrão nulo) + +### show-info + +Um valor que indica se uma informação extra sobre o início, fim e total de registros será exibida. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Popovers.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Popovers.md new file mode 100644 index 0000000000..1fd346b8ce --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Popovers.md @@ -0,0 +1,40 @@ +# Popovers + +## Introdução + +`abp-popover` é a tag abp para mensagens de popover. + +Uso básico: + +````xml +<abp-button abp-popover="Oi, eu sou o conteúdo do popover!"> + Popover Padrão +</abp-button> +```` + +## Demonstração + +Veja a página de demonstração de [popovers](https://bootstrap-taghelpers.abp.io/Components/Popovers) para vê-lo em ação. + +## Atributos + +### disabled + +Um valor que indica se o elemento deve ser desabilitado para interação. Se esse valor for definido como `true`, o atributo `dismissable` será ignorado. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### dismissable + +Um valor que indica se os popovers devem ser fechados no próximo clique do usuário em um elemento diferente do elemento de ativação. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### hoverable + +Um valor que indica se o conteúdo do popover será exibido ao passar o mouse sobre ele. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Progress-Bars.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Progress-Bars.md new file mode 100644 index 0000000000..6630edf473 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Progress-Bars.md @@ -0,0 +1,68 @@ +# Barras de Progresso + +## Introdução + +`abp-progress-bar` é a tag abp para o status da barra de progresso. + +Uso básico: + +````xml +<abp-progress-bar value="70" /> + +<abp-progress-bar type="Warning" value="25"> %25 </abp-progress-bar> + +<abp-progress-bar type="Success" value="40" strip="true"/> + +<abp-progress-bar type="Dark" value="10" min-value="5" max-value="15" strip="true"> %50 </abp-progress-bar> + +<abp-progress-group> + <abp-progress-part type="Success" value="25"/> + <abp-progress-part type="Danger" value="10" strip="true"> %10 </abp-progress-part> + <abp-progress-part type="Primary" value="50" animation="true" strip="true" /> +</abp-progress-group> +```` + +## Demonstração + +Veja a página de demonstração das [barras de progresso](https://bootstrap-taghelpers.abp.io/Components/Progressbars) para vê-las em ação. + +## Atributos + +### value + +Um valor que indica o progresso atual da barra. + +### type + +Um valor que indica a cor de fundo da barra de progresso. Deve ser um dos seguintes valores: + +* `Default` (valor padrão) +* `Secondary` +* `Success` +* `Danger` +* `Warning` +* `Info` +* `Light` +* `Dark` + +### min-value + +Valor mínimo da barra de progresso. O padrão é 0. + +### max-value + +Valor máximo da barra de progresso. O padrão é 100. + +### strip + +Um valor que indica se o estilo de fundo da barra de progresso é listrado. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` + +### animation + +Um valor que indica se o estilo de fundo listrado da barra de progresso é animado. Deve ser um dos seguintes valores: + +* `false` (valor padrão) +* `true` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tables.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tables.md new file mode 100644 index 0000000000..8d3e8f2612 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tables.md @@ -0,0 +1,61 @@ +# Tabelas + +## Introdução + +`abp-table` é o componente básico de tag para tabelas no abp. + +Uso básico: + +````html +<abp-table hoverable-rows="true" responsive-sm="true"> + <thead> + <tr> + <th scope="Column">#</th> + <th scope="Column">Primeiro</th> + <th scope="Column">Último</th> + <th scope="Column">Identificador</th> + </tr> + </thead> + <tbody> + <tr> + <th scope="Row">1</th> + <td>Mark</td> + <td>Otto</td> + <td table-style="Danger">mdo</td> + </tr> + <tr table-style="Warning"> + <th scope="Row">2</th> + <td>Jacob</td> + <td>Thornton</td> + <td>fat</td> + </tr> + <tr> + <th scope="Row">3</th> + <td table-style="Success">Larry</td> + <td>the Bird</td> + <td>twitter</td> + </tr> + </tbody> + </abp-table> +```` + + + +## Demonstração + +Veja a página de demonstração de [tabelas](https://bootstrap-taghelpers.abp.io/Components/Tables) para vê-la em ação. + +## Atributos do abp-table + +- **responsive**: Usado para criar tabelas responsivas até um determinado ponto de interrupção. Veja [específico do ponto de interrupção](https://getbootstrap.com/docs/4.1/content/tables/#breakpoint-specific) para mais informações. +- **responsive-sm**: Se não for definido como false, define a responsividade da tabela para dispositivos de tela pequena. +- **responsive-md**: Se não for definido como false, define a responsividade da tabela para dispositivos de tela média. +- **responsive-lg**: Se não for definido como false, define a responsividade da tabela para dispositivos de tela grande. +- **responsive-xl**: Se não for definido como false, define a responsividade da tabela para dispositivos de tela extra grande. +- **dark-theme**: Se definido como true, define o tema de cor da tabela como escuro. +- **striped-rows**: Se definido como true, adiciona listras zebradas às linhas da tabela. +- **hoverable-rows**: Se definido como true, adiciona estado de hover às linhas da tabela. +- **border-style**: Define o estilo da borda da tabela. Deve ser um dos seguintes valores: + - `Default` (padrão) + - `Bordered` + - `Borderless` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tabs.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tabs.md new file mode 100644 index 0000000000..dbf3b213f8 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tabs.md @@ -0,0 +1,91 @@ +# Abas + +## Introdução + +`abp-tab` é o contêiner básico de conteúdo de navegação por abas derivado do elemento de aba do Bootstrap. + +Uso básico: + +````xml +<abp-tabs> + <abp-tab title="Home"> + Conteúdo_Início + </abp-tab> + <abp-tab-link title="Link" href="#" /> + <abp-tab title="perfil"> + Conteúdo_Perfil + </abp-tab> + <abp-tab-dropdown title="Contato" name="DropdownContato"> + <abp-tab title="Contato 1" parent-dropdown-name="DropdownContato"> + Conteúdo_1_Conteúdo + </abp-tab> + <abp-tab title="Contato 2" parent-dropdown-name="DropdownContato"> + Conteúdo_2_Conteúdo + </abp-tab> + </abp-tab-dropdown> +</abp-tabs> +```` + +## Demonstração + +Veja a [página de demonstração de abas](https://bootstrap-taghelpers.abp.io/Components/Tabs) para vê-la em ação. + +## Atributos do abp-tab + +- **title**: Define o texto do menu da aba. +- **name:** Define o atributo "id" dos elementos gerados. O valor padrão é um Guid. Não é necessário, a menos que as abas sejam alteradas ou modificadas com Jquery. +- **active**: Define a aba ativa. + +Exemplo: + +````xml +<abp-tabs name="IdDaAba"> + <abp-tab name="nav-inicio" title="Home"> + Conteúdo_Início + </abp-tab> + <abp-tab name="nav-perfil" active="true" title="perfil"> + Conteúdo_Perfil + </abp-tab> + <abp-tab name="nav-contato" title="Contato"> + Conteúdo_Contato + </abp-tab> +</abp-tabs> +```` + +### Pílulas + +Exemplo: + +````xml +<abp-tabs tab-style="Pill"> + <abp-tab title="Home"> + Conteúdo_Início + </abp-tab> + <abp-tab title="perfil"> + Conteúdo_Perfil + </abp-tab> + <abp-tab title="Contato"> + Conteúdo_Contato + </abp-tab> +</abp-tabs> +```` + +### Vertical + +**vertical-header-size**: Define a largura da coluna dos cabeçalhos das abas. + +Exemplo: + +````xml +<abp-tabs tab-style="PillVertical" vertical-header-size="_2" > + <abp-tab active="true" title="Home"> + Conteúdo_Início + </abp-tab> + <abp-tab title="perfil"> + Conteúdo_Perfil + </abp-tab> + <abp-tab title="Contato"> + Conteúdo_Contato + </abp-tab> +</abp-tabs> +```` \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tooltips.md b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tooltips.md new file mode 100644 index 0000000000..794e36b119 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Tag-Helpers/Tooltips.md @@ -0,0 +1,35 @@ +# Dicas de Ferramentas + +## Introdução + +`abp-tooltip` é a tag abp para dicas de ferramentas. + +Uso básico: + +````xml +<abp-button abp-tooltip="Dica de Ferramenta"> + Dica de Ferramenta Padrão +</abp-button> + +<abp-button abp-tooltip-top="Dica de Ferramenta"> + Dica de Ferramenta no topo +</abp-button> + +<abp-button abp-tooltip-right="Dica de Ferramenta"> + Dica de Ferramenta à direita +</abp-button> + +<abp-button abp-tooltip-bottom="Dica de Ferramenta"> + Dica de Ferramenta na parte inferior +</abp-button> + +<abp-button disabled="true" abp-tooltip="Dica de Ferramenta"> + Dica de Ferramenta do botão desativado +</abp-button> +```` + + + +## Demonstração + +Veja a página de demonstração de [dicas de ferramentas](https://bootstrap-taghelpers.abp.io/Components/Tooltips) para vê-la em ação. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Testing.md b/docs/pt-BR/UI/AspNetCore/Testing.md new file mode 100644 index 0000000000..2e5afaf65b --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Testing.md @@ -0,0 +1,207 @@ +# ASP.NET Core MVC / Razor Pages: Testando + +> Você pode seguir a documentação de Testes de Integração do ASP.NET Core (ASP.NET Core Integration Tests) para aprender detalhes sobre os testes de integração do ASP.NET Core. Este documento explica a infraestrutura de teste adicional fornecida pelo ABP Framework. + +## O Modelo de Inicialização do Aplicativo + +O Modelo de Inicialização do Aplicativo contém o projeto `.Web` que contém as visualizações/páginas/componentes de interface do usuário do aplicativo e um projeto `.Web.Tests` para testá-los. + +![aspnetcore-web-tests-in-solution](../../images/aspnetcore-web-tests-in-solution.png) + +## Testando as Páginas Razor + +Suponha que você tenha criado uma Página Razor chamada `Issues.cshtml` com o seguinte conteúdo; + +**Issues.cshtml.cs** + +````csharp +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.RazorPages; +using MyProject.Issues; + +namespace MyProject.Web.Pages +{ + public class IssuesModel : PageModel + { + public List<IssueDto> Issues { get; set; } + + private readonly IIssueAppService _issueAppService; + + public IssuesModel(IIssueAppService issueAppService) + { + _issueAppService = issueAppService; + } + + public async Task OnGetAsync() + { + Issues = await _issueAppService.GetListAsync(); + } + } +} +```` + +**Issues.cshtml** + +````html +@page +@model MyProject.Web.Pages.IssuesModel +<h2>Lista de Problemas</h2> +<table id="IssueTable" class="table"> + <thead> + <tr> + <th>Problema</th> + <th>Fechado?</th> + </tr> + </thead> + <tbody> + @foreach (var issue in Model.Issues) + { + <tr> + <td>@issue.Title</td> + <td> + @if (issue.IsClosed) + { + <span>Fechado</span> + } + else + { + <span>Aberto</span> + } + </td> + </tr> + } + </tbody> +</table> +```` + +Esta página simplesmente cria uma tabela com os problemas: + +![issue-list](../../images/issue-list.png) + +Você pode escrever uma classe de teste dentro do projeto `.Web.Tests` da mesma forma que o exemplo abaixo: + +````csharp +using System.Threading.Tasks; +using HtmlAgilityPack; +using Shouldly; +using Xunit; + +namespace MyProject.Pages +{ + public class Issues_Tests : MyProjectWebTestBase + { + [Fact] + public async Task Should_Get_Table_Of_Issues() + { + // Agir + + var response = await GetResponseAsStringAsync("/Issues"); + + // Assert + + var htmlDocument = new HtmlDocument(); + htmlDocument.LoadHtml(response); + + var tableElement = htmlDocument.GetElementbyId("IssueTable"); + tableElement.ShouldNotBeNull(); + + var trNodes = tableElement.SelectNodes("//tbody/tr"); + trNodes.Count.ShouldBeGreaterThan(0); + } + } +} +```` + +`GetResponseAsStringAsync` é um método de atalho que vem da classe base que realiza uma solicitação HTTP GET, verifica se o status HTTP resultante é `200` e retorna a resposta como uma `string`. + +> Você pode usar o objeto base `Client` (do tipo `HttpClient`) para realizar qualquer tipo de solicitação ao servidor e ler a resposta você mesmo. `GetResponseAsStringAsync` é apenas um método de atalho. + +Este exemplo usa a biblioteca [HtmlAgilityPack](https://html-agility-pack.net/) para analisar o HTML recebido e testar se ele contém a tabela de problemas. + +> Este exemplo pressupõe que existam alguns problemas iniciais no banco de dados. Consulte a seção *The Data Seed* do documento de Testes (Testing document) para aprender como inserir dados de teste, para que seus testes possam assumir que alguns dados iniciais estão disponíveis no banco de dados. + +## Testando os Controladores + +Testar um controlador não é diferente. Basta fazer uma solicitação ao servidor com uma URL adequada, obter a resposta e fazer suas asserções. + +### Resultado de Visualização + +Se o controlador retornar uma visualização, você pode usar um código semelhante para testar o HTML retornado. Veja o exemplo das Páginas Razor acima. + +### Resultado de Objeto + +Se o controlador retornar um resultado de objeto, você pode usar o método base `GetResponseAsObjectAsync`. + +Suponha que você tenha um controlador definido da seguinte forma: + +````csharp +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using MyProject.Issues; +using Volo.Abp.AspNetCore.Mvc; + +namespace MyProject.Web.Controllers +{ + [Route("api/issues")] + public class IssueController : AbpController + { + private readonly IIssueAppService _issueAppService; + + public IssueController(IIssueAppService issueAppService) + { + _issueAppService = issueAppService; + } + + [HttpGet] + public async Task<List<IssueDto>> GetAsync() + { + return await _issueAppService.GetListAsync(); + } + } +} +```` + +Você pode escrever um código de teste para executar a API e obter o resultado: + +````csharp +using System.Collections.Generic; +using System.Threading.Tasks; +using MyProject.Issues; +using Shouldly; +using Xunit; + +namespace MyProject.Pages +{ + public class Issues_Tests : MyProjectWebTestBase + { + [Fact] + public async Task Should_Get_Issues_From_Api() + { + var issues = await GetResponseAsObjectAsync<List<IssueDto>>("/api/issues"); + + issues.ShouldNotBeNull(); + issues.Count.ShouldBeGreaterThan(0); + } + } +} +```` + +## Testando o Código JavaScript + +O ABP Framework não fornece nenhuma infraestrutura para testar seu código JavaScript. Você pode usar qualquer estrutura de teste e ferramentas para testar seu código JavaScript. + +## A Infraestrutura de Teste + +O pacote [Volo.Abp.AspNetCore.TestBase](https://www.nuget.org/packages/Volo.Abp.AspNetCore.TestBase) fornece a infraestrutura de teste que está integrada ao ABP Framework e ao ASP.NET Core. + +> O pacote Volo.Abp.AspNetCore.TestBase já está instalado no projeto `.Web.Tests`. + +Este pacote fornece a classe base `AbpWebApplicationFactoryIntegratedTest` como a classe base fundamental para derivar as classes de teste. Ela é herdada da classe [WebApplicationFactory](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests) fornecida pelo ASP.NET Core. + +A classe base `MyProjectWebTestBase` usada acima herda da `AbpWebApplicationFactoryIntegratedTest`, então herdamos indiretamente a `AbpWebApplicationFactoryIntegratedTest`. + +Veja também +* [Testes de integração no ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests) +* [Testes Gerais / Lado do Servidor](../../Testing.md) \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Theming.md b/docs/pt-BR/UI/AspNetCore/Theming.md new file mode 100644 index 0000000000..8cb2684c12 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Theming.md @@ -0,0 +1,442 @@ +# ASP.NET Core MVC / Razor Pages: Tematização de UI + +## Introdução + +O ABP Framework fornece um sistema completo de **tematização de UI** com os seguintes objetivos: + +* Módulos de aplicativos reutilizáveis são desenvolvidos de forma **independente de tema**, para que possam funcionar com qualquer tema de UI. +* O tema de UI é **decidido pela aplicação final**. +* O tema é distribuído por meio de pacotes NuGet/NPM, para que seja **facilmente atualizável**. +* A aplicação final pode **personalizar** o tema selecionado. + +Para alcançar esses objetivos, o ABP Framework: + +* Determina um conjunto de **bibliotecas base** usadas e adaptadas por todos os temas. Assim, os desenvolvedores de módulos e aplicativos podem depender e usar essas bibliotecas sem depender de um tema específico. +* Fornece um sistema que consiste em [menus de navegação](../../Navigation-Menu.md), [barras de ferramentas](../../Toolbars.md), [hooks de layout](../../Layout-Hooks.md)... que são implementados por todos os temas. Assim, os módulos e a aplicação contribuem para o layout para compor uma UI de aplicação consistente. + +### Temas Atuais + +Atualmente, quatro temas são **oficialmente fornecidos**: + +* O [Tema Básico](../../Basic-Theme.md) é o tema minimalista com o estilo Bootstrap simples. É **open source e gratuito**. +* O [Tema LeptonX Lite](../../Themes/LeptonXLite/AspNetCore.md) é um tema moderno e elegante de UI Bootstrap. É ideal se você deseja ter um tema de UI pronto para produção. Também é **open source e gratuito**. +* O [Tema Lepton](https://commercial.abp.io/themes) é um tema **comercial** desenvolvido pela equipe central do ABP e faz parte da licença [ABP Commercial](https://commercial.abp.io/). +* O [Tema LeptonX](https://docs.abp.io/en/commercial/latest/themes/lepton-x/index) também é um tema **comercial** desenvolvido pela equipe central do ABP e faz parte da licença [ABP Commercial](https://commercial.abp.io/). Este é o tema padrão após o ABP v6.0.0. + +Também existem alguns temas desenvolvidos pela comunidade para o ABP Framework (você pode pesquisar na web). + +## Geral + +### As Bibliotecas Base + +Todos os temas devem depender do pacote NPM [@abp/aspnetcore.mvc.ui.theme.shared](https://www.npmjs.com/package/@abp/aspnetcore.mvc.ui.theme.shared), para que dependam indiretamente das seguintes bibliotecas: + +* [Twitter Bootstrap](https://getbootstrap.com/) como o framework HTML/CSS fundamental. +* [JQuery](https://jquery.com/) para manipulação do DOM. +* [DataTables.Net](https://datatables.net/) para grades de dados. +* [JQuery Validation](https://github.com/jquery-validation/jquery-validation) para validação do lado do cliente e [unobtrusive](https://github.com/aspnet/jquery-validation-unobtrusive) validation +* [FontAwesome](https://fontawesome.com/) como a biblioteca fundamental de fontes CSS. +* [SweetAlert](https://sweetalert.js.org/) para exibir mensagens de alerta e caixas de diálogo de confirmação. +* [Toastr](https://github.com/CodeSeven/toastr) para exibir notificações de toast. +* [Lodash](https://lodash.com/) como uma biblioteca de utilitários. +* [Luxon](https://moment.github.io/luxon/) para operações de data/hora. +* [JQuery Form](https://github.com/jquery-form/form) para formulários AJAX. +* [bootstrap-datepicker](https://github.com/uxsolutions/bootstrap-datepicker) para exibir seletores de data. +* [Select2](https://select2.org/) para caixas de seleção/combo melhores. +* [Timeago](http://timeago.yarp.com/) para exibir carimbos de data/hora fuzzy atualizados automaticamente. +* [malihu-custom-scrollbar-plugin](https://github.com/malihu/malihu-custom-scrollbar-plugin) para barras de rolagem personalizadas. + +Essas bibliotecas são selecionadas como as bibliotecas base e estão disponíveis para as aplicações e módulos. + +#### Abstrações / Wrappers + +Existem algumas abstrações no ABP Framework para tornar seu código independente de algumas dessas bibliotecas também. Exemplos: + +* [Tag Helpers](../../Tag-Helpers/Index.md) facilitam a geração de UIs do Bootstrap. +* As APIs JavaScript [Message](../../JavaScript-API/Message.md) e [Notification](../../JavaScript-API/Notify.md) fornecem abstrações para usar o Sweetalert e o Toastr. +* O sistema de [Forms & Validation](../../Forms-Validation.md) manipula automaticamente a validação, então você geralmente não digita diretamente nenhum código de validação. + +### Os Layouts Padrão + +A principal responsabilidade de um tema é fornecer os layouts. Existem **três layouts predefinidos que devem ser implementados por todos os temas**: + +* **Application**: O layout padrão usado pelas páginas principais do aplicativo. +* **Account**: Geralmente usado pelo [módulo de conta](../../Modules/Account.md) para páginas de login, registro, esqueci minha senha... +* **Empty**: O layout mínimo que não possui componentes de layout. + +Os nomes dos layouts são constantes definidas na classe `Volo.Abp.AspNetCore.Mvc.UI.Theming.StandardLayouts`. + +#### O Layout de Aplicativo + +Este é o layout padrão usado pelas páginas principais do aplicativo. A imagem a seguir mostra a página de gerenciamento de usuários no layout de aplicativo do [Tema Básico](../../Basic-Theme.md): + +![basic-theme-application-layout](../../images/basic-theme-application-layout.png) + +E a mesma página é mostrada abaixo com o layout de aplicativo do [Tema Lepton](https://commercial.abp.io/themes): + +![lepton-theme-application-layout](../../images/lepton-theme-application-layout.png) + +Como você pode ver, a página é a mesma, mas a aparência é completamente diferente nos temas acima. + +O layout de aplicativo normalmente inclui as seguintes partes: + +* Um [menu principal](../../Navigation-Menu.md) +* Uma [barra de ferramentas](../../Toolbars.md) principal com os seguintes componentes: + * Menu do usuário + * Dropdown de troca de idioma +* [Alertas de página](../../Page-Alerts.md) +* O conteúdo da página (chamado de `RenderBody()`) +* [Hooks de layout](../../Layout-Hooks.md) + +Alguns temas podem fornecer mais partes, como breadcrumbs, cabeçalho e barra de ferramentas da página... etc. Veja a seção *Partes do Layout*. + +#### O Layout de Conta + +O layout de conta é geralmente usado pelo [módulo de conta](../../Modules/Account.md) para páginas de login, registro, esqueci minha senha... + +![basic-theme-account-layout](../../images/basic-theme-account-layout.png) + +Este layout normalmente fornece as seguintes partes: + +* Dropdown de troca de idioma +* Área de troca de locatário (se a aplicação for [multi-tenant](../../Multi-Tenancy.md) e o locatário atual for resolvido pelo cookie) +* [Alertas de página](../../Page-Alerts.md) +* O conteúdo da página (chamado de `RenderBody()`) +* [Hooks de layout](../../Layout-Hooks.md) + +O [Tema Básico](../../Basic-Theme.md) também renderiza a barra de navegação superior para este layout (como mostrado acima). + +Aqui está o layout de conta do Tema Lepton: + +![lepton-theme-account-layout](../../images/lepton-theme-account-layout.png) + +O [Tema Lepton](https://commercial.abp.io/themes) mostra o logotipo do aplicativo e o rodapé neste layout. + +> Você pode substituir completamente ou parcialmente os layouts do tema em um aplicativo para [personalizá-lo](../../Customization-User-Interface.md). + +#### O Layout Vazio + +O layout vazio fornece uma página vazia. Normalmente inclui as seguintes partes: + +* [Alertas de página](../../Page-Alerts.md) +* O conteúdo da página (chamado de `RenderBody()`) +* [Hooks de layout](../../Layout-Hooks.md) + +## Implementando um Tema + +### A Forma Mais Fácil + +A forma mais fácil de criar um novo tema é adicionar o módulo [Código-fonte do Tema Básico](https://github.com/abpframework/abp/tree/dev/modules/basic-theme) com os códigos-fonte e personalizá-lo. + +```bash +abp add-package Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic --with-source-code --add-to-solution-file +``` + +### A Interface ITheme + +A interface `ITheme` é usada pelo ABP Framework para selecionar o layout para a página atual. Um tema deve implementar esta interface para fornecer o caminho do layout solicitado. + +Esta é a implementação da `ITheme` do [Tema Básico](../../Basic-Theme.md). + +````csharp +using Volo.Abp.AspNetCore.Mvc.UI.Theming; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic +{ + [ThemeName(Name)] + public class BasicTheme : ITheme, ITransientDependency + { + public const string Name = "Basic"; + + public virtual string GetLayout(string name, bool fallbackToDefault = true) + { + switch (name) + { + case StandardLayouts.Application: + return "~/Themes/Basic/Layouts/Application.cshtml"; + case StandardLayouts.Account: + return "~/Themes/Basic/Layouts/Account.cshtml"; + case StandardLayouts.Empty: + return "~/Themes/Basic/Layouts/Empty.cshtml"; + default: + return fallbackToDefault + ? "~/Themes/Basic/Layouts/Application.cshtml" + : null; + } + } + } +} +```` + +* O atributo `[ThemeName]` é obrigatório e um tema deve ter um nome único, `Basic` neste exemplo. +* O método `GetLayout` deve retornar um caminho se o layout solicitado (`name`) for fornecido pelo tema. *Os Layouts Padrão* devem ser implementados se o tema for destinado a ser usado por um aplicativo padrão. Ele pode implementar layouts adicionais. + +Depois que o tema implementa a interface `ITheme`, ele deve adicionar o tema às `AbpThemingOptions` no método `ConfigureServices` do [módulo](../../Module-Development-Basics.md). + +````csharp +Configure<AbpThemingOptions>(options => +{ + options.Themes.Add<BasicTheme>(); +}); +```` + +#### O Serviço IThemeSelector + +O ABP Framework permite usar vários temas juntos. É por isso que `options.Themes` é uma lista. O serviço `IThemeSelector` seleciona o tema em tempo de execução. O desenvolvedor da aplicação pode definir `AbpThemingOptions.DefaultThemeName` para definir o tema a ser usado ou substituir a implementação do serviço `IThemeSelector` (a implementação padrão é `DefaultThemeSelector`) para controlar completamente a seleção do tema em tempo de execução. + +### Bundles + +O [sistema de agrupamento](../../Bundling-Minification.md) fornece uma maneira padrão de importar arquivos de estilo e script nas páginas. Existem dois pacotes padrão definidos pelo ABP Framework: + +* `StandardBundles.Styles.Global`: O pacote global que inclui os arquivos de estilo usados em todas as páginas. Normalmente, inclui os arquivos CSS das Bibliotecas Base. +* `StandardBundles.Scripts.Global`: O pacote global que inclui os arquivos de script usados em todas as páginas. Normalmente, inclui os arquivos JavaScript das Bibliotecas Base. + +Um tema geralmente estende esses pacotes padrão adicionando arquivos CSS/JavaScript específicos do tema. + +A melhor maneira de definir novos pacotes é herdar dos pacotes padrão e adicioná-los às `AbpBundlingOptions`, como mostrado abaixo (este código é do [Tema Básico](../../Basic-Theme.md)): + +````csharp +Configure<AbpBundlingOptions>(options => +{ + options + .StyleBundles + .Add(BasicThemeBundles.Styles.Global, bundle => + { + bundle + .AddBaseBundles(StandardBundles.Styles.Global) + .AddContributors(typeof(BasicThemeGlobalStyleContributor)); + }); + + options + .ScriptBundles + .Add(BasicThemeBundles.Scripts.Global, bundle => + { + bundle + .AddBaseBundles(StandardBundles.Scripts.Global) + .AddContributors(typeof(BasicThemeGlobalScriptContributor)); + }); +}); +```` + +`BasicThemeGlobalStyleContributor` e `BasicThemeGlobalScriptContributor` são contribuidores de pacotes. Por exemplo, `BasicThemeGlobalStyleContributor` é definido como mostrado abaixo: + +```csharp +public class BasicThemeGlobalStyleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.Add("/themes/basic/layout.css"); + } +} +``` + +Em seguida, o tema pode renderizar esses pacotes em um layout. Por exemplo, você pode renderizar os Estilos Globais como mostrado abaixo: + +````html +<abp-style-bundle name="@BasicThemeBundles.Styles.Global" /> +```` + +Consulte o documento [Bundle & Minification](../../Bundling-Minification.md) para entender melhor o sistema de agrupamento. + +### Partes do Layout + +Um layout típico consiste em várias partes. O tema deve incluir as partes necessárias em cada layout. + +**Exemplo: O Tema Básico tem as seguintes partes para o Layout de Aplicativo** + +![basic-theme-application-layout-parts](../../images/basic-theme-application-layout-parts.png) + +O código do aplicativo e dos módulos só pode mostrar conteúdo na parte de Conteúdo da Página. Se eles precisarem alterar as outras partes (adicionar um item de menu, adicionar um item de barra de ferramentas, alterar o nome do aplicativo na área de marcação...), eles devem usar as APIs do ABP Framework. + +As seções a seguir explicam as partes fundamentais pré-definidas pelo ABP Framework e que podem ser implementadas pelos temas. + +> É uma boa prática dividir o layout em componentes/partials, para que o aplicativo final possa substituí-los parcialmente para fins de personalização. + +#### Marcação + +O serviço `IBrandingProvider` deve ser usado para obter o nome e a URL do logotipo do aplicativo para renderizar na parte de Marcação. + +O [Modelo de Inicialização do Aplicativo](../../Startup-Templates/Application.md) tem uma implementação dessa interface para definir os valores pelo desenvolvedor da aplicação. + +#### Menu Principal + +O serviço `IMenuManager` é usado para obter os itens do menu principal e renderizá-los no layout. + +**Exemplo: Obter o Menu Principal para renderizar em um componente de visualização** + +```csharp +public class MainNavbarMenuViewComponent : AbpViewComponent +{ + private readonly IMenuManager _menuManager; + + public MainNavbarMenuViewComponent(IMenuManager menuManager) + { + _menuManager = menuManager; + } + + public async Task<IViewComponentResult> InvokeAsync() + { + var menu = await _menuManager.GetAsync(StandardMenus.Main); + return View("~/Themes/Basic/Components/Menu/Default.cshtml", menu); + } +} +``` + +Consulte o documento [Navegação / Menus](../../Navigation-Menu.md) para saber mais sobre o sistema de navegação. + +#### Barra de Ferramentas Principal + +O serviço `IToolbarManager` é usado para obter os itens da Barra de Ferramentas Principal e renderizá-los no layout. Cada item desta barra de ferramentas é um componente de visualização, então ele pode incluir qualquer tipo de elemento de UI. Injete o `IToolbarManager` e use o `GetAsync` para obter os itens da barra de ferramentas: + +````csharp +var toolbar = await _toolbarManager.GetAsync(StandardToolbars.Main); +```` + +> Consulte o documento [Barras de Ferramentas](../../Toolbars.md) para saber mais sobre o sistema de barras de ferramentas. + +O tema tem a responsabilidade de adicionar dois itens pré-definidos à barra de ferramentas principal: Seleção de Idioma e Menu do Usuário. Para fazer isso, crie uma classe que implemente a interface `IToolbarContributor` e adicione-a às `AbpToolbarOptions`, como mostrado abaixo: + +```csharp +Configure<AbpToolbarOptions>(options => +{ + options.Contributors.Add(new BasicThemeMainTopToolbarContributor()); +}); +``` + +##### Seleção de Idioma + +O item de barra de ferramentas Seleção de Idioma é geralmente um dropdown que é usado para alternar entre idiomas. O `ILanguageProvider` é usado para obter a lista de idiomas disponíveis e `CultureInfo.CurrentUICulture` é usado para saber o idioma atual. + +O endpoint `/Abp/Languages/Switch` pode ser usado para alternar o idioma. Este endpoint aceita os seguintes parâmetros de string de consulta: + +* `culture`: O idioma selecionado, como `en-US` ou `en`. +* `uiCulture`: O idioma de IU selecionado, como `en-US` ou `en`. +* `returnUrl` (opcional): Pode ser usado para retornar uma determinada URL após a troca de idioma. + +`culture` e `uiCulture` devem corresponder a um dos idiomas disponíveis. O ABP Framework define um cookie de cultura no endpoint `/Abp/Languages/Switch`. + +##### Menu do Usuário + +O menu do usuário inclui links relacionados à conta do usuário. O `IMenuManager` é usado da mesma forma que o Menu Principal, mas desta vez com o parâmetro `StandardMenus.User`, como mostrado abaixo: + +````csharp +var menu = await _menuManager.GetAsync(StandardMenus.User); +```` + +Os serviços [ICurrentUser](../../CurrentUser.md) e [ICurrentTenant](../../Multi-Tenancy.md) podem ser usados para obter os nomes do usuário e do locatário atual. + +#### Alertas de Página + +O serviço `IAlertManager` é usado para obter os alertas de página atuais para renderizar no layout. Use a lista `Alerts` do `IAlertManager`. Geralmente é renderizado logo antes do conteúdo da página (`RenderBody()`). + +Consulte o documento [Alertas de Página](../../Page-Alerts.md) para saber mais. + +#### Hooks de Layout + +Como o Layout está no pacote do tema, o aplicativo final ou qualquer módulo não pode manipular diretamente o conteúdo do layout. O sistema de [Hooks de Layout](../../Layout-Hooks.md) permite injetar componentes em pontos específicos do layout. + +O tema é responsável por renderizar os hooks no local correto. + +**Exemplo: Renderizar o Hook `LayoutHooks.Head.First` no Layout de Aplicativo** + +````html +<head> + @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Application) + ... +```` + +Consulte o documento [Hooks de Layout](../../Layout-Hooks.md) para saber mais sobre os hooks de layout padrão. + +#### Seções de Script / Estilo + +Todo layout deve renderizar as seguintes seções opcionais: + +* A seção `styles` é renderizada no final do `head`, logo antes do `LayoutHooks.Head.Last`. +* A seção `scripts` é renderizada no final do `body`, logo antes do `LayoutHooks.Body.Last`. + +Dessa forma, a página pode importar estilos e scripts para o layout. + +**Exemplo: Renderizar a seção `styles`** + +````csharp +@await RenderSectionAsync("styles", required: false) +```` + +#### Seção de Barra de Ferramentas de Conteúdo + +Outra seção pré-definida é a seção de Barra de Ferramentas de Conteúdo, que pode ser usada pelas páginas para adicionar código logo antes do conteúdo da página. O Tema Básico a renderiza da seguinte forma: + +````html +<div id="AbpContentToolbar"> + <div class="text-end mb-2"> + @RenderSection("content_toolbar", false) + </div> +</div> +```` + +O id da div de contêiner deve ser `AbpContentToolbar`. Esta seção deve vir antes do `RenderBody()`. + +#### Recursos de Widgets + +O [Sistema de Widgets](../../Widgets.md) permite definir widgets reutilizáveis com seus próprios arquivos de estilo/script. Todos os layouts devem renderizar o estilo e os scripts do widget. + +**Estilos de Widget** são renderizados da seguinte forma, logo antes da seção `styles`, após o pacote de estilo global: + +````csharp +@await Component.InvokeAsync(typeof(WidgetStylesViewComponent)) +```` + +**Scripts de Widget** são renderizados da seguinte forma, logo antes da seção `scripts`, após o pacote de script global: + +````csharp +@await Component.InvokeAsync(typeof(WidgetScriptsViewComponent)) +```` + +#### Scripts ABP + +O ABP tem alguns scripts especiais que devem ser incluídos em todos os layouts. Eles não estão incluídos nos pacotes globais, pois são criados dinamicamente com base no usuário atual. + +Os scripts do ABP (`ApplicationConfigurationScript` e `ServiceProxyScript`) devem ser adicionados logo após o pacote de script global, como mostrado abaixo: + +````html +<script src="~/Abp/ApplicationConfigurationScript"></script> +<script src="~/Abp/ServiceProxyScript"></script> +```` + +#### Título da Página, Item de Menu Selecionado e Breadcrumbs + +O serviço `IPageLayout` pode ser injetado por qualquer página para definir o título da página, o nome do item de menu selecionado e os itens de breadcrumbs. Em seguida, o tema pode usar este serviço para obter esses valores e renderizá-los na UI. + +O Tema Básico não implementa este serviço, mas o Tema Lepton implementa: + +![breadcrumbs-example](../../images/breadcrumbs-example.png) + +Consulte o documento [Cabeçalho da Página](../../Page-Header.md) para saber mais. + +#### Troca de Locatário + +O Layout de Conta deve permitir ao usuário trocar o locatário atual se a aplicação for multi-tenant e o locatário for resolvido a partir dos cookies. Consulte o [Layout de Conta do Tema Básico](https://github.com/abpframework/abp/blob/dev/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml) como um exemplo de implementação. + +### Classes de Layout + +Os Layouts Padrão (`Application`, `Account` e `Empty`) devem adicionar as seguintes classes CSS à tag `body`: + +* `abp-application-layout` para o layout `Application`. +* `abp-account-layout` para o layout `Account`. +* `abp-empty-layout` para o layout `Empty`. + +Dessa forma, os aplicativos ou módulos podem ter seletores com base no layout atual. + +### RTL + +Para oferecer suporte a idiomas da direita para a esquerda, o Layout deve verificar a cultura atual e adicionar `dir="rtl"` à tag `html` e a classe CSS `rtl` à tag `body`. + +Você pode verificar `CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft` para entender se o idioma atual é um idioma RTL. + +### O Pacote NPM + +Um tema deve ter um pacote NPM que dependa do pacote [@abp/aspnetcore.mvc.ui.theme.shared](https://www.npmjs.com/package/@abp/aspnetcore.mvc.ui.theme.shared). Dessa forma, ele herda todas as Bibliotecas Base. Se o tema exigir bibliotecas adicionais, ele deve definir essas dependências também. + +As aplicações usam o sistema de [Gerenciamento de Pacotes do Lado do Cliente](../../Client-Side-Package-Management.md) para adicionar bibliotecas do lado do cliente ao projeto. Portanto, se uma aplicação usar seu tema, ela deve adicionar a dependência do pacote NPM do seu tema, bem como a dependência do pacote NuGet. + diff --git a/docs/pt-BR/UI/AspNetCore/Toolbars.md b/docs/pt-BR/UI/AspNetCore/Toolbars.md new file mode 100644 index 0000000000..8bf5af4b4e --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Toolbars.md @@ -0,0 +1,95 @@ +# ASP.NET Core MVC / Razor Pages UI: Barras de Ferramentas + +O sistema de barras de ferramentas é usado para definir **barras de ferramentas** na interface do usuário. Módulos (ou sua aplicação) podem adicionar **itens** a uma barra de ferramentas e, em seguida, o [tema](Theming.md) renderiza a barra de ferramentas no **layout**. + +Existe apenas uma **barra de ferramentas padrão** chamada "Principal" (definida como uma constante: `StandardToolbars.Main`). O [Tema Básico](Basic-Theme) renderiza a barra de ferramentas principal como mostrado abaixo: + +![bookstore-toolbar-highlighted](../../images/bookstore-toolbar-highlighted.png) + +Na captura de tela acima, existem dois itens adicionados à barra de ferramentas principal: o componente de alternância de idioma e o menu do usuário. Você pode adicionar seus próprios itens aqui. + +Além disso, o [Tema LeptonX Lite](../../Themes/LeptonXLite/AspNetCore.md) possui duas barras de ferramentas diferentes para visualizações de desktop e móveis, definidas como constantes: `LeptonXLiteToolbars.Main`, `LeptonXLiteToolbars.MainMobile`. + +| LeptonXLiteToolbars.Main | LeptonXLiteToolbars.MainMobile | +| :---: | :---: | +| ![leptonx](../../images/leptonxlite-toolbar-main-example.png) | ![leptonx](../../images/leptonxlite-toolbar-mainmobile-example.png) | + +## Exemplo: Adicionar um Ícone de Notificação + +Neste exemplo, vamos adicionar um **ícone de notificação (sino)** à esquerda do item de alternância de idioma. Um item na barra de ferramentas deve ser um **componente de visualização**. Então, primeiro, crie um novo componente de visualização em seu projeto: + +![bookstore-notification-view-component](../../images/bookstore-notification-view-component.png) + +**NotificationViewComponent.cs** + +````csharp +public class NotificationViewComponent : AbpViewComponent +{ + public async Task<IViewComponentResult> InvokeAsync() + { + return View("/Pages/Shared/Components/Notification/Default.cshtml"); + } +} +```` + +**Default.cshtml** + +````xml +<div id="MainNotificationIcon" style="color: white; margin: 8px;"> + <i class="far fa-bell"></i> +</div> +```` + +Agora, podemos criar uma classe que implementa a interface `IToolbarContributor`: + +````csharp +public class MyToolbarContributor : IToolbarContributor +{ + public Task ConfigureToolbarAsync(IToolbarConfigurationContext context) + { + if (context.Toolbar.Name == StandardToolbars.Main) + { + context.Toolbar.Items + .Insert(0, new ToolbarItem(typeof(NotificationViewComponent))); + } + + return Task.CompletedTask; + } +} +```` + +Você pode usar a [autorização](../../Authorization.md) para decidir se deve adicionar um `ToolbarItem`. + +````csharp +if (await context.IsGrantedAsync("NomeDaMinhaPermissao")) +{ + //...adicionar itens da barra de ferramentas +} +```` + +Você pode usar o método de extensão `RequirePermissions` como um atalho. Também é mais eficiente, o ABP otimiza a verificação de permissão para todos os itens. + +````csharp +context.Toolbar.Items.Insert(0, new ToolbarItem(typeof(NotificationViewComponent)).RequirePermissions("NomeDaMinhaPermissao")); +```` + +Essa classe adiciona o `NotificationViewComponent` como o primeiro item na barra de ferramentas `Main`. + +Por fim, você precisa adicionar esse contribuidor às `AbpToolbarOptions`, no `ConfigureServices` do seu [módulo](../../Module-Development-Basics.md): + +````csharp +Configure<AbpToolbarOptions>(options => +{ + options.Contributors.Add(new MyToolbarContributor()); +}); +```` + +Isso é tudo, você verá o ícone de notificação na barra de ferramentas quando executar a aplicação: + +![bookstore-notification-icon-on-toolbar](../../images/bookstore-notification-icon-on-toolbar.png) + +`NotificationViewComponent` neste exemplo simplesmente retorna uma visualização sem nenhum dado. Na vida real, provavelmente você desejará **consultar o banco de dados** (ou chamar uma API HTTP) para obter notificações e passá-las para a visualização. Se necessário, você pode adicionar um arquivo `JavaScript` ou `CSS` ao [pacote](Bundling-Minification.md) global para o item da barra de ferramentas. + +## IToolbarManager + +`IToolbarManager` é usado para renderizar a barra de ferramentas. Ele retorna os itens da barra de ferramentas por um nome de barra de ferramentas. Isso é geralmente usado pelos [temas](Theming.md) para renderizar a barra de ferramentas no layout. \ No newline at end of file diff --git a/docs/pt-BR/UI/AspNetCore/Widgets.md b/docs/pt-BR/UI/AspNetCore/Widgets.md new file mode 100644 index 0000000000..215ebf3528 --- /dev/null +++ b/docs/pt-BR/UI/AspNetCore/Widgets.md @@ -0,0 +1,526 @@ +# Widgets + +ABP fornece um modelo e infraestrutura para criar **widgets reutilizáveis**. O sistema de widgets é uma extensão dos [Componentes de Visualização do ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components). Os widgets são especialmente úteis quando você deseja: + +* Ter dependências de **scripts e estilos** para o seu widget. +* Criar **painéis de controle** com widgets usados dentro. +* Definir widgets em **[módulos](../../Module-Development-Basics.md)** reutilizáveis. +* Cooperar com widgets com sistemas de **[autorização](../../Authorization.md)** e **[agrupamento](Bundling-Minification.md)**. + +## Definição Básica de Widget + +### Criar um Componente de Visualização + +Como primeiro passo, crie um novo Componente de Visualização regular do ASP.NET Core: + +![widget-basic-files](../../images/widget-basic-files.png) + +**MySimpleWidgetViewComponent.cs**: + +````csharp +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace DashboardDemo.Web.Pages.Components.MySimpleWidget +{ + public class MySimpleWidgetViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View(); + } + } +} +```` + +Herdar de `AbpViewComponent` não é obrigatório. Você pode herdar do `ViewComponent` padrão do ASP.NET Core. `AbpViewComponent` apenas define algumas propriedades úteis básicas. + +Você pode injetar um serviço e usá-lo no método `Invoke` para obter alguns dados do serviço. Talvez seja necessário tornar o método Invoke assíncrono, como `public async Task<IViewComponentResult> InvokeAsync()`. Consulte o documento [Componentes de Visualização do ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) para ver todos os diferentes usos. + +**Default.cshtml**: + +```xml +<div class="my-simple-widget"> + <h2>My Simple Widget</h2> + <p>This is a simple widget!</p> +</div> +``` + +### Definir o Widget + +Adicione um atributo `Widget` à classe `MySimpleWidgetViewComponent` para marcar este componente de visualização como um widget: + +````csharp +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace DashboardDemo.Web.Pages.Components.MySimpleWidget +{ + [Widget] + public class MySimpleWidgetViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View(); + } + } +} +```` + +## Renderizando um Widget + +Renderizar um widget é bastante padrão. Use o método `Component.InvokeAsync` em uma view/página razor como você faria para qualquer componente de visualização. Exemplos: + +````xml +@await Component.InvokeAsync("MySimpleWidget") +@await Component.InvokeAsync(typeof(MySimpleWidgetViewComponent)) +```` + +A primeira abordagem usa o nome do widget, enquanto a segunda abordagem usa o tipo do componente de visualização. + +### Widgets com Argumentos + +O sistema de componentes de visualização do ASP.NET Core permite que você aceite argumentos para componentes de visualização. O exemplo de componente de visualização abaixo aceita `startDate` e `endDate` e usa esses argumentos para recuperar dados de um serviço. + +````csharp +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace DashboardDemo.Web.Pages.Shared.Components.CountersWidget +{ + [Widget] + public class CountersWidgetViewComponent : AbpViewComponent + { + private readonly IDashboardAppService _dashboardAppService; + + public CountersWidgetViewComponent(IDashboardAppService dashboardAppService) + { + _dashboardAppService = dashboardAppService; + } + + public async Task<IViewComponentResult> InvokeAsync( + DateTime startDate, DateTime endDate) + { + var result = await _dashboardAppService.GetCountersWidgetAsync( + new CountersWidgetInputDto + { + StartDate = startDate, + EndDate = endDate + } + ); + + return View(result); + } + } +} +```` + +Agora, você precisa passar um objeto anônimo para passar argumentos, como mostrado abaixo: + +````xml +@await Component.InvokeAsync("CountersWidget", new +{ + startDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)), + endDate = DateTime.Now +}) +```` + +## Nome do Widget + +O nome padrão dos componentes de visualização é calculado com base no nome do tipo do componente de visualização. Se o tipo do componente de visualização for `MySimpleWidgetViewComponent`, então o nome do widget será `MySimpleWidget` (remove o sufixo `ViewComponent`). É assim que o ASP.NET Core calcula o nome de um componente de visualização. + +Para personalizar o nome do widget, basta usar o atributo `ViewComponent` padrão do ASP.NET Core: + +```csharp +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace DashboardDemo.Web.Pages.Components.MySimpleWidget +{ + [Widget] + [ViewComponent(Name = "MyCustomNamedWidget")] + public class MySimpleWidgetViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View("~/Pages/Components/MySimpleWidget/Default.cshtml"); + } + } +} +``` + +O ABP respeitará o nome personalizado ao lidar com o widget. + +> Se o nome do componente de visualização e o nome da pasta do componente de visualização não corresponderem, talvez seja necessário escrever manualmente o caminho da visualização, como feito neste exemplo. + +### Nome de Exibição + +Você também pode definir um nome de exibição legível por humanos e localizável para o widget. Esse nome de exibição pode ser usado na interface do usuário quando necessário. O nome de exibição é opcional e pode ser definido usando as propriedades do atributo `Widget`: + +````csharp +using DashboardDemo.Localization; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace DashboardDemo.Web.Pages.Components.MySimpleWidget +{ + [Widget( + DisplayName = "MySimpleWidgetDisplayName", //Chave de localização + DisplayNameResource = typeof(DashboardDemoResource) //recurso de localização + )] + public class MySimpleWidgetViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View(); + } + } +} +```` + +Consulte [o documento de localização](../../Localization.md) para saber mais sobre recursos e chaves de localização. + +## Dependências de Estilo e Script + +Existem alguns desafios quando seu widget possui arquivos de script e estilo; + +* Qualquer página que usa o widget também deve incluir os arquivos de **script e estilos** dele na página. +* A página também deve se preocupar com as **bibliotecas/arquivos dependentes** do widget. + +O ABP resolve esses problemas quando você relaciona corretamente os recursos com o widget. Você não precisa se preocupar com as dependências do widget ao usá-lo. + +### Definindo como Caminhos Simples de Arquivo + +O widget de exemplo abaixo adiciona um arquivo de estilo e um arquivo de script: + +````csharp +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace DashboardDemo.Web.Pages.Components.MySimpleWidget +{ + [Widget( + StyleFiles = new[] { "/Pages/Components/MySimpleWidget/Default.css" }, + ScriptFiles = new[] { "/Pages/Components/MySimpleWidget/Default.js" } + )] + public class MySimpleWidgetViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View(); + } + } +} +```` + +O ABP leva em consideração essas dependências e as adiciona corretamente à visualização/página quando você usa o widget. Os arquivos de estilo/script podem ser **físicos ou virtuais**. Está completamente integrado ao [Sistema de Arquivos Virtual](../../Virtual-File-System.md). + +### Definindo Contribuidores de Pacote + +Todos os recursos para os widgets usados em uma página são adicionados como um **pacote** (agrupados e minificados em produção se você não configurar de outra forma). Além de adicionar um arquivo simples, você pode aproveitar ao máximo os contribuidores de pacote. + +O código de exemplo abaixo faz o mesmo que o código acima, mas define e usa contribuidores de pacote: + +````csharp +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace DashboardDemo.Web.Pages.Components.MySimpleWidget +{ + [Widget( + StyleTypes = new []{ typeof(MySimpleWidgetStyleBundleContributor) }, + ScriptTypes = new[]{ typeof(MySimpleWidgetScriptBundleContributor) } + )] + public class MySimpleWidgetViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View(); + } + } + + public class MySimpleWidgetStyleBundleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files + .AddIfNotContains("/Pages/Components/MySimpleWidget/Default.css"); + } + } + + public class MySimpleWidgetScriptBundleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files + .AddIfNotContains("/Pages/Components/MySimpleWidget/Default.js"); + } + } +} + +```` + +O sistema de contribuição de pacotes é muito poderoso. Se o seu widget usa uma biblioteca JavaScript para renderizar um gráfico, você pode declará-la como uma dependência, para que a biblioteca JavaScript seja automaticamente adicionada à página se ainda não tiver sido adicionada. Dessa forma, a página que usa seu widget não se preocupa com as dependências. + +Consulte a documentação de [agrupamento e minificação](Bundling-Minification.md) para obter mais informações sobre esse sistema. + +## RefreshUrl + +Um widget pode projetar uma `RefreshUrl` que é usada sempre que o widget precisa ser atualizado. Se for definido, o widget é renderizado novamente no lado do servidor em cada atualização (consulte o método de atualização do `WidgetManager` abaixo). + +````csharp +[Widget(RefreshUrl = "Widgets/Counters")] +public class CountersWidgetViewComponent : AbpViewComponent +{ + +} +```` + +Depois de definir uma `RefreshUrl` para o seu widget, você precisa fornecer um endpoint para renderizá-lo e retorná-lo: + +````csharp +[Route("Widgets")] +public class CountersWidgetController : AbpController +{ + [HttpGet] + [Route("Counters")] + public IActionResult Counters(DateTime startDate, DateTime endDate) + { + return ViewComponent("CountersWidget", new {startDate, endDate}); + } +} +```` + +A rota `Widgets/Counters` corresponde à `RefreshUrl` declarada anteriormente. + +> Um widget deve ser atualizado de duas maneiras: Na primeira maneira, quando você usa uma `RefreshUrl`, ele é renderizado novamente no servidor e substituído pelo HTML retornado pelo servidor. Na segunda maneira, o widget obtém dados (geralmente um objeto JSON) do servidor e atualiza-se no lado do cliente (consulte o método de atualização na seção Widget JavaScript API). + +## AutoInitialize + +O atributo `Widget` tem uma propriedade `AutoInitialize` (`bool`) que pode ser definida como `true` para inicializar automaticamente um widget quando a página estiver pronta e sempre que o widget for adicionado ao DOM. O valor padrão é `false`. + +Se um widget estiver configurado para ser inicializado automaticamente, então um `WidgetManager` (consulte abaixo) é criado e inicializado automaticamente para instâncias deste widget. Isso é útil quando as instâncias do widget não estão agrupadas e funcionam separadamente (não é necessário inicializar ou atualizar juntas). + +Definir o `AutoInitialize` como `true` é equivalente a escrever esse código você mesmo: + +````js +$('.abp-widget-wrapper[data-widget-name="MySimpleWidget"]') + .each(function () { + var widgetManager = new abp.WidgetManager({ + wrapper: $(this), + }); + + widgetManager.init($(this)); + }); +```` + +> `AutoInitialize` também suporta widgets carregados/atualizados via AJAX (adicionados ao DOM posteriormente) e/ou usados de forma aninhada (um widget dentro de outro widget). Se você não precisa agrupar vários widgets e controlar com um único `WidgetManager`, `AutoInitialize` é a abordagem recomendada. + +## API JavaScript do Widget + +Um widget pode precisar ser renderizado e atualizado no lado do cliente. Nesses casos, você pode usar o `WidgetManager` do ABP e definir APIs para seus widgets. + +### WidgetManager + +O `WidgetManager` é usado para inicializar e atualizar um ou mais widgets. Crie um novo `WidgetManager` da seguinte forma: + +````js +$(function() { + var myWidgetManager = new abp.WidgetManager('#MyDashboardWidgetsArea'); +}) +```` + +`MyDashboardWidgetsArea` pode conter um ou mais widgets dentro. + +> Usar o `WidgetManager` dentro do document.ready (como acima) é uma boa prática, pois suas funções usam o DOM e precisam que o DOM esteja pronto. + +#### WidgetManager.init() + +`init` simplesmente inicializa o `WidgetManager` e chama os métodos `init` dos widgets relacionados, se eles estiverem definidos (consulte a seção Widget JavaScript API abaixo) + +```js +myWidgetManager.init(); +``` + +#### WidgetManager.refresh() + +O método `refresh` atualiza todos os widgets relacionados a este `WidgetManager`: + +```` +myWidgetManager.refresh(); +```` + +#### Opções do WidgetManager + +O `WidgetManager` tem algumas opções adicionais. + +##### Filtro do Formulário + +Se seus widgets exigirem parâmetros/filtros, você geralmente terá um formulário para filtrar os widgets. Nesses casos, você pode criar um formulário que tenha alguns elementos de formulário e uma área de painel de controle com alguns widgets dentro. Exemplo: + +````xml +<form method="get" id="MyDashboardFilterForm"> + ...elementos do formulário +</form> + +<div id="MyDashboardWidgetsArea" data-widget-filter="#MyDashboardFilterForm"> + ...widgets +</div> +```` + +O atributo `data-widget-filter` relaciona o formulário com os widgets. Sempre que o formulário for enviado, todos os widgets serão atualizados automaticamente com os campos do formulário como filtro. + +Em vez do atributo `data-widget-filter`, você pode usar o parâmetro `filterForm` do construtor do `WidgetManager`. Exemplo: + +````js +var myWidgetManager = new abp.WidgetManager({ + wrapper: '#MyDashboardWidgetsArea', + filterForm: '#MyDashboardFilterForm' +}); +```` + +##### Callback do Filtro + +Você pode querer ter um controle melhor para fornecer filtros ao inicializar e atualizar os widgets. Nesse caso, você pode usar a opção `filterCallback`: + +````js +var myWidgetManager = new abp.WidgetManager({ + wrapper: '#MyDashboardWidgetsArea', + filterCallback: function() { + return $('#MyDashboardFilterForm').serializeFormToObject(); + } +}); +```` + +Este exemplo mostra a implementação padrão do `filterCallback`. Você pode retornar qualquer objeto JavaScript com campos. Exemplo: + +````js +filterCallback: function() { + return { + 'startDate': $('#StartDateInput').val(), + 'endDate': $('#EndDateInput').val() + }; +} +```` + +Os filtros retornados são passados para todos os widgets em `init` e `refresh`. + +### API JavaScript do Widget + +Um widget pode definir uma API JavaScript que é invocada pelo `WidgetManager` quando necessário. O exemplo de código abaixo pode ser usado para começar a definir uma API para um widget. + +````js +(function () { + abp.widgets.NewUserStatisticWidget = function ($wrapper) { + + var getFilters = function () { + return { + ... + }; + } + + var refresh = function (filters) { + ... + }; + + var init = function (filters) { + ... + }; + + return { + getFilters: getFilters, + init: init, + refresh: refresh + }; + }; +})(); +```` + +`NewUserStatisticWidget` é o nome do widget aqui. Ele deve corresponder ao nome do widget definido no lado do servidor. Todas as funções são opcionais. + +#### getFilters + +Se o widget tiver filtros personalizados internos, essa função deve retornar o objeto de filtro. Exemplo: + +````js +var getFilters = function() { + return { + frequency: $wrapper.find('.frequency-filter option:selected').val() + }; +} +```` + +Este método é usado pelo `WidgetManager` ao construir filtros. + +#### init + +Usado para inicializar o widget quando necessário. Ele tem um argumento de filtro que pode ser usado ao obter dados do servidor. O método `init` é usado quando a função `WidgetManager.init()` é chamada. Também é chamado se o seu widget requer uma recarga completa na atualização. Consulte a opção de widget `RefreshUrl`. + +#### refresh + +Usado para atualizar o widget quando necessário. Ele tem um argumento de filtro que pode ser usado ao obter dados do servidor. O método `refresh` é usado sempre que a função `WidgetManager.refresh()` é chamada. + +## Autorização + +Alguns widgets podem estar disponíveis apenas para usuários autenticados ou autorizados. Nesse caso, use as seguintes propriedades do atributo `Widget`: + +* `RequiresAuthentication` (`bool`): Defina como true para tornar este widget utilizável apenas para usuários autenticados (usuários que fizeram login na aplicação). +* `RequiredPolicies` (`List<string>`): Uma lista de nomes de políticas para autorizar o usuário. Consulte [o documento de autorização](../../Authorization.md) para obter mais informações sobre políticas. + +Exemplo: + +````csharp +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace DashboardDemo.Web.Pages.Components.MySimpleWidget +{ + [Widget(RequiredPolicies = new[] { "MyPolicyName" })] + public class MySimpleWidgetViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View(); + } + } +} +```` + +## WidgetOptions + +Como alternativa ao atributo `Widget`, você pode usar o `AbpWidgetOptions` para configurar widgets: + +```csharp +Configure<AbpWidgetOptions>(options => +{ + options.Widgets.Add<MySimpleWidgetViewComponent>(); +}); +``` + +Escreva isso no método `ConfigureServices` do seu [módulo](../../Module-Development-Basics.md). Todas as configurações feitas com o atributo `Widget` também são possíveis com o `AbpWidgetOptions`. Exemplo de configuração que adiciona um estilo para o widget: + +````csharp +Configure<AbpWidgetOptions>(options => +{ + options.Widgets + .Add<MySimpleWidgetViewComponent>() + .WithStyles("/Pages/Components/MySimpleWidget/Default.css"); +}); +```` + +> Dica: `AbpWidgetOptions` também pode ser usado para obter um widget existente e alterar sua configuração. Isso é especialmente útil se você deseja modificar a configuração de um widget dentro de um módulo usado por sua aplicação. Use `options.Widgets.Find` para obter uma `WidgetDefinition` existente. + +## Veja também + +* [Projeto de exemplo (código-fonte)](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo). \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/DatatablesNet/DatatablesNetScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/DatatablesNet/DatatablesNetScriptContributor.cs index daea15328f..2ad3bb2bdb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/DatatablesNet/DatatablesNetScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/DatatablesNet/DatatablesNetScriptContributor.cs @@ -10,6 +10,14 @@ public class DatatablesNetScriptContributor : BundleContributor { public override void ConfigureBundle(BundleConfigurationContext context) { - context.Files.AddIfNotContains("/libs/datatables.net/js/jquery.dataTables.js"); + if (context.FileProvider.GetFileInfo("/libs/datatables.net/js/dataTables.min.js").Exists) + { + context.Files.AddIfNotContains("/libs/datatables.net/js/dataTables.min.js"); + } + else + { + //TODO: Compatible code, Remove it after 8.3 rc1. + context.Files.AddIfNotContains("/libs/datatables.net/js/jquery.dataTables.js"); + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js index b4000d6cbc..9b5b29d70b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js @@ -240,10 +240,17 @@ var abp = abp || {}; if (tableInstance.aoColumns) { columns = tableInstance.aoColumns; - } else { + } else if (abp.utils.isFunction(tableInstance.fnSettings)) { columns = tableInstance.fnSettings().aoColumns; } + if (!columns && abp.utils.isFunction(tableInstance.api)) { + var settings = tableInstance.api().settings(); + if (settings.length === 1 && settings[0].aoColumns) { + columns = settings[0].aoColumns; + } + } + if (!columns) { return; } @@ -271,8 +278,9 @@ var abp = abp || {}; } }; - var _existingApiRenderRowActionsFunction = $.fn.dataTableExt.oApi.renderRowActions; - $.fn.dataTableExt.oApi.renderRowActions = + if ($.fn.dataTableExt.oApi) { + var _existingApiRenderRowActionsFunction = $.fn.dataTableExt.oApi.renderRowActions; + $.fn.dataTableExt.oApi.renderRowActions = function (tableInstance, nRow, aData, iDisplayIndex, iDisplayIndexFull) { if (_existingApiRenderRowActionsFunction) { _existingApiRenderRowActionsFunction(tableInstance, nRow, aData, iDisplayIndex, iDisplayIndexFull); @@ -280,7 +288,8 @@ var abp = abp || {}; renderRowActions(tableInstance, nRow, aData, iDisplayIndex, iDisplayIndexFull); }; - + } + if (!$.fn.dataTable) { return; } diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs index 593992aed4..730fe98533 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs @@ -102,6 +102,7 @@ public static class AbpApplicationBuilderExtensions return app.UseMiddleware<AbpExceptionHandlingMiddleware>(); } + [Obsolete("Replace with AbpClaimsTransformation")] public static IApplicationBuilder UseAbpClaimsMap(this IApplicationBuilder app) { return app.UseMiddleware<AbpClaimsMapMiddleware>(); diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs index bda87a4e32..c6f8dd7ddc 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs @@ -1,6 +1,7 @@ -using System.Linq; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; +using Volo.Abp.AspNetCore.Security.Claims; namespace Microsoft.Extensions.DependencyInjection; @@ -20,4 +21,9 @@ public static class AbpAspNetCoreServiceCollectionExtensions return hostingEnvironment; } + + public static IServiceCollection TransformAbpClaims(this IServiceCollection services) + { + return services.AddTransient<IClaimsTransformation, AbpClaimsTransformation>(); + } } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs index bb2bedf901..2104edab0a 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -10,6 +11,7 @@ using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Security.Claims; +[Obsolete("Replace with AbpClaimsTransformation")] public class AbpClaimsMapMiddleware : AbpMiddlewareBase, ITransientDependency { public async override Task InvokeAsync(HttpContext context, RequestDelegate next) diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsTransformation.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsTransformation.cs new file mode 100644 index 0000000000..17e8f0d599 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsTransformation.cs @@ -0,0 +1,35 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Options; + +namespace Volo.Abp.AspNetCore.Security.Claims; + +public class AbpClaimsTransformation : IClaimsTransformation +{ + protected IOptions<AbpClaimsMapOptions> AbpClaimsMapOptions { get; } + + public AbpClaimsTransformation(IOptions<AbpClaimsMapOptions> abpClaimsMapOptions) + { + AbpClaimsMapOptions = abpClaimsMapOptions; + } + + public virtual Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal) + { + var mapClaims = principal.Claims.Where(claim => AbpClaimsMapOptions.Value.Maps.Keys.Contains(claim.Type)); + + principal.AddIdentity(new ClaimsIdentity(mapClaims.Select( + claim => new Claim( + AbpClaimsMapOptions.Value.Maps[claim.Type](), + claim.Value, + claim.ValueType, + claim.Issuer + ) + ) + ) + ); + + return Task.FromResult(principal); + } +} diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor index 96ee31a9f1..ae87961e21 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor @@ -45,9 +45,9 @@ <EntityAction TItem="TItem" Color="@(action.Color != null ? (Blazorise.Color)action.Color : Blazorise.Color.Primary)" Icon="@action.Icon" - Clicked="async () => await action.Clicked(context)" - ConfirmationMessage="() => action.ConfirmationMessage.Invoke(context)" - Visible="@(action.Visible != null ? action.Visible(context) : true)" + Clicked="async () => await action.Clicked(context!)" + ConfirmationMessage="() => action.ConfirmationMessage.Invoke(context!)" + Visible="@(action.Visible != null ? action.Visible(context!) : true)" Text="@action.Text" Disabled="@action.Disabled"> </EntityAction> @@ -55,10 +55,10 @@ else { <EntityAction TItem="TItem" - Clicked="async () => await action.Clicked(context)" + Clicked="async () => await action.Clicked(context!)" Color="@(action.Color != null ? (Blazorise.Color)action.Color : Blazorise.Color.Primary)" Icon="@action.Icon" - Visible="@(action.Visible != null ? action.Visible(context) : true)" + Visible="@(action.Visible != null ? action.Visible(context!) : true)" Text="@action.Text" Disabled="@action.Disabled"> </EntityAction> @@ -76,7 +76,7 @@ { <DataGridColumn TItem="TItem" Field="@column.Data" Caption="@column.Title" Sortable="@column.Sortable" DisplayFormat="@column.DisplayFormat" DisplayFormatProvider="@column.DisplayFormatProvider" Displayable="column.Visible" > <DisplayTemplate> - @RenderCustomTableColumnComponent(column.Component, context) + @RenderCustomTableColumnComponent(column.Component, context!) </DisplayTemplate> </DataGridColumn> } @@ -84,7 +84,7 @@ { <DataGridColumn TItem="TItem" Field="@column.Data" Caption="@column.Title" Sortable="@column.Sortable" Displayable="column.Visible"> <DisplayTemplate> - @RenderCustomTableColumnComponent(column.Component, context) + @RenderCustomTableColumnComponent(column.Component, context!) </DisplayTemplate> </DataGridColumn> } diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/ObjectExtending/LookupExtensionProperty.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/ObjectExtending/LookupExtensionProperty.razor index ecdae979f2..6217a75543 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/ObjectExtending/LookupExtensionProperty.razor +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/ObjectExtending/LookupExtensionProperty.razor @@ -10,7 +10,7 @@ TItem="SelectItem<object>" TValue="object" TextField="item => item?.Text!" - ValueField="item => item?.Value" + ValueField="item => item?.Value!" SelectedValue="@SelectedValue" SelectedValueChanged="@SelectedValueChanged" SearchChanged="@SearchFilterChangedAsync" diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 638860e742..138bf82ec4 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -192,9 +192,9 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext, { try { - if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges) + foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries()) { - foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries()) + if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges) { if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted) { @@ -205,6 +205,23 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext, EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity); } } + else if (entityEntry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd))) + { + if (entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey())) + { + // Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed. + break; + } + + if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted) + { + EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity); + } + else + { + EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity); + } + } } var auditLog = AuditingManager?.Current?.Log; diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs index 21e663e5e1..ebc04605c6 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs @@ -53,7 +53,8 @@ public class AbpEventBusModule : AbpModule { localHandlers.Add(context.ImplementationType); } - else if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IDistributedEventHandler<>))) + + if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IDistributedEventHandler<>))) { distributedHandlers.Add(context.ImplementationType); } diff --git a/framework/src/Volo.Abp.Swashbuckle/Microsoft/Extensions/DependencyInjection/AbpSwaggerGenOptionsExtensions.cs b/framework/src/Volo.Abp.Swashbuckle/Microsoft/Extensions/DependencyInjection/AbpSwaggerGenOptionsExtensions.cs index 240da0fc4e..fe592d6b45 100644 --- a/framework/src/Volo.Abp.Swashbuckle/Microsoft/Extensions/DependencyInjection/AbpSwaggerGenOptionsExtensions.cs +++ b/framework/src/Volo.Abp.Swashbuckle/Microsoft/Extensions/DependencyInjection/AbpSwaggerGenOptionsExtensions.cs @@ -1,4 +1,6 @@ -using Swashbuckle.AspNetCore.SwaggerGen; +using System; +using System.Linq; +using Swashbuckle.AspNetCore.SwaggerGen; using Volo.Abp.Swashbuckle; namespace Microsoft.Extensions.DependencyInjection; @@ -14,4 +16,22 @@ public static class AbpSwaggerGenOptionsExtensions { swaggerGenOptions.SchemaFilter<AbpSwashbuckleEnumSchemaFilter>(); } -} \ No newline at end of file + + public static void CustomAbpSchemaIds(this SwaggerGenOptions options) + { + string SchemaIdSelector(Type modelType) + { + if (!modelType.IsConstructedGenericType) + { + return modelType.FullName!.Replace("[]", "Array"); + } + + var prefix = modelType.GetGenericArguments() + .Select(SchemaIdSelector) + .Aggregate((previous, current) => previous + current); + return modelType.FullName!.Split('`').First() + "Of" + prefix; + } + + options.CustomSchemaIds(SchemaIdSelector); + } +} diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json index be6b1a94a2..edf6b38b16 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json @@ -41,7 +41,7 @@ "Selected": "kiválasztott", "PagerShowMenuEntries": "Megjelenítve _MENU_ elem", "DatatableActionDropdownDefaultText": "Műveletek", - "ChangePassword": "Jelszó véltoztatás", + "ChangePassword": "Jelszó változtatás", "PersonalInfo": "Saját profil", "AreYouSureYouWantToCancelEditingWarningMessage": "Vannak mentetlen változtatásai.", "GoHomePage": "Kezdőlapra lépés", diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs index 3f9c605f64..027403d226 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs @@ -73,9 +73,10 @@ public class AbpAspNetCoreMvcTestModule : AbpModule context.Services.AddAuthentication(options => { + options.DefaultAuthenticateScheme = FakeAuthenticationSchemeDefaults.Scheme; options.DefaultChallengeScheme = "Bearer"; options.DefaultForbidScheme = "Cookie"; - }).AddCookie("Cookie").AddJwtBearer("Bearer", _ => { }); + }).AddFakeAuthentication().AddCookie("Cookie").AddJwtBearer("Bearer", _ => { }); context.Services.AddAuthorization(options => { @@ -137,6 +138,8 @@ public class AbpAspNetCoreMvcTestModule : AbpModule { options.Contributors.Add(new TestApplicationConfigurationContributor()); }); + + context.Services.TransformAbpClaims(); } public override void OnApplicationInitialization(ApplicationInitializationContext context) @@ -148,8 +151,6 @@ public class AbpAspNetCoreMvcTestModule : AbpModule app.UseAbpRequestLocalization(); app.UseAbpSecurityHeaders(); app.UseRouting(); - app.UseMiddleware<FakeAuthenticationMiddleware>(); - app.UseAbpClaimsMap(); app.UseAuthentication(); app.UseAuthorization(); app.UseAuditing(); diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationMiddleware.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationMiddleware.cs index a3cd4a57e6..886e22f1dd 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationMiddleware.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationMiddleware.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; @@ -8,6 +9,7 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Mvc; +[Obsolete("Use FakeAuthenticationScheme instead.")] public class FakeAuthenticationMiddleware : AbpMiddlewareBase, ITransientDependency { private readonly FakeUserClaims _fakeUserClaims; diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs new file mode 100644 index 0000000000..641bbe03d7 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs @@ -0,0 +1,68 @@ +using System; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using DeviceDetectorNET; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Volo.Abp.AspNetCore.Mvc; + +public static class FakeAuthenticationSchemeDefaults +{ + public static string Scheme => "FakeAuthenticationScheme"; +} + +public static class FakeAuthenticationBuilderExtensions +{ + public static AuthenticationBuilder AddFakeAuthentication(this AuthenticationBuilder builder) + { + return builder.AddScheme<FakeAuthenticationOptions, FakeAuthenticationHandler>(FakeAuthenticationSchemeDefaults.Scheme, _ => { }); + } +} + +public class FakeAuthenticationOptions : AuthenticationSchemeOptions +{ + +} + +public class FakeAuthenticationHandler : AuthenticationHandler<FakeAuthenticationOptions> +{ + private readonly FakeUserClaims _fakeUserClaims; + + [Obsolete] + public FakeAuthenticationHandler( + IOptionsMonitor<FakeAuthenticationOptions> options, + ILoggerFactory logger, + UrlEncoder encoder, + ISystemClock clock, + FakeUserClaims fakeUserClaims) : base(options, logger, encoder, clock) + { + _fakeUserClaims = fakeUserClaims; + } + + public FakeAuthenticationHandler( + IOptionsMonitor<FakeAuthenticationOptions> options, + ILoggerFactory logger, + UrlEncoder encoder, + FakeUserClaims fakeUserClaims) + : base(options, logger, encoder) + { + _fakeUserClaims = fakeUserClaims; + } + + protected override Task<AuthenticateResult> HandleAuthenticateAsync() + { + if (_fakeUserClaims.Claims.Any()) + { + return Task.FromResult(AuthenticateResult.Success( + new AuthenticationTicket( + new ClaimsPrincipal(new ClaimsIdentity(_fakeUserClaims.Claims, + FakeAuthenticationSchemeDefaults.Scheme)), + FakeAuthenticationSchemeDefaults.Scheme))); + } + + return Task.FromResult(AuthenticateResult.NoResult()); + } +} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs index 225e767d26..0992a904ad 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs @@ -28,6 +28,8 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext> public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; } + public DbSet<AppEntityWithNavigationsForeign> AppEntityWithNavigationsForeign { get; set; } + public TestMigrationsDbContext(DbContextOptions<TestMigrationsDbContext> options) : base(options) { @@ -74,6 +76,7 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext> b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOne>(x => x.Id); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId); b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>(); + b.HasOne<AppEntityWithNavigationsForeign>().WithMany().HasForeignKey(x => x.AppEntityWithNavigationForeignId).IsRequired(false); }); modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b => @@ -87,5 +90,10 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext> b.ConfigureByConvention(); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationChildOneToManyId); }); + + modelBuilder.Entity<AppEntityWithNavigationsForeign>(b => + { + b.ConfigureByConvention(); + }); } } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs index e668a21e26..fcaf8e5f7a 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs @@ -36,6 +36,8 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext, public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; } + public DbSet<AppEntityWithNavigationsForeign> AppEntityWithNavigationsForeign { get; set; } + public TestAppDbContext(DbContextOptions<TestAppDbContext> options) : base(options) { @@ -101,6 +103,7 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext, b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOne>(x => x.Id); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId); b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>(); + b.HasOne<AppEntityWithNavigationsForeign>().WithMany().HasForeignKey(x => x.AppEntityWithNavigationForeignId).IsRequired(false); }); modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b => @@ -115,6 +118,11 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext, b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationChildOneToManyId); }); + modelBuilder.Entity<AppEntityWithNavigationsForeign>(b => + { + b.ConfigureByConvention(); + }); + modelBuilder.TryConfigureObjectExtensions<TestAppDbContext>(); } } diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/LocalAndDistributeEventHandlerRegister_Tests.cs b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/LocalAndDistributeEventHandlerRegister_Tests.cs new file mode 100644 index 0000000000..c6f912ba33 --- /dev/null +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/LocalAndDistributeEventHandlerRegister_Tests.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Shouldly; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.EventBus.Local; +using Xunit; + +namespace Volo.Abp.EventBus; + +public class LocalAndDistributeEventHandlerRegister_Tests : EventBusTestBase +{ + [Fact] + public void Should_Register_Both_Local_And_Distribute() + { + var localOptions = GetRequiredService<IOptions<AbpLocalEventBusOptions>>(); + var distributedOptions = GetRequiredService<IOptions<AbpDistributedEventBusOptions>>(); + + localOptions.Value.Handlers.ShouldContain(x => x == typeof(MyEventHandle)); + distributedOptions.Value.Handlers.ShouldContain(x => x == typeof(MyEventHandle)); + } + + class MyEventDate + { + + } + + class MyEventHandle : ILocalEventHandler<MyEventDate>, IDistributedEventHandler<MyEventDate>, ITransientDependency + { + Task ILocalEventHandler<MyEventDate>.HandleEventAsync(MyEventDate eventData) + { + return Task.CompletedTask; + } + + Task IDistributedEventHandler<MyEventDate>.HandleEventAsync(MyEventDate eventData) + { + return Task.CompletedTask; + } + } +} diff --git a/framework/test/Volo.Abp.Imaging.Abstractions.Tests/Volo/Abp/Imaging/Files/abp.webp b/framework/test/Volo.Abp.Imaging.Abstractions.Tests/Volo/Abp/Imaging/Files/abp.webp index 8d61a9ffda..70b613b8f2 100644 Binary files a/framework/test/Volo.Abp.Imaging.Abstractions.Tests/Volo/Abp/Imaging/Files/abp.webp and b/framework/test/Volo.Abp.Imaging.Abstractions.Tests/Volo/Abp/Imaging/Files/abp.webp differ diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs index e9b9706e0c..2653948163 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs @@ -23,6 +23,7 @@ public class AppEntityWithNavigations : AggregateRoot<Guid> public string FullName { get; set; } + public AppEntityWithValueObjectAddress AppEntityWithValueObjectAddress { get; set; } public virtual AppEntityWithNavigationChildOneToOne OneToOne { get; set; } @@ -30,6 +31,8 @@ public class AppEntityWithNavigations : AggregateRoot<Guid> public virtual List<AppEntityWithNavigationChildOneToMany> OneToMany { get; set; } public virtual List<AppEntityWithNavigationChildManyToMany> ManyToMany { get; set; } + + public virtual Guid? AppEntityWithNavigationForeignId { get; set; } } public class AppEntityWithValueObjectAddress : ValueObject @@ -88,3 +91,19 @@ public class AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany public Guid AppEntityWithNavigationChildManyToManyId { get; set; } } + +public class AppEntityWithNavigationsForeign : AggregateRoot<Guid> +{ + protected AppEntityWithNavigationsForeign() + { + + } + + public AppEntityWithNavigationsForeign(Guid id, string name) + : base(id) + { + Name = name; + } + + public string Name { get; set; } +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs index 7db0ba180d..e5e3ad5cb6 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs @@ -191,11 +191,13 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule> where TStartupModule : IAbpModule { protected readonly IRepository<AppEntityWithNavigations, Guid> AppEntityWithNavigationsRepository; + protected readonly IRepository<AppEntityWithNavigationsForeign, Guid> AppEntityWithNavigationForeignRepository; protected readonly ILocalEventBus LocalEventBus; protected AbpEntityChangeOptions_DomainEvents_Tests() { AppEntityWithNavigationsRepository = GetRequiredService<IRepository<AppEntityWithNavigations, Guid>>(); + AppEntityWithNavigationForeignRepository = GetRequiredService<IRepository<AppEntityWithNavigationsForeign, Guid>>(); LocalEventBus = GetRequiredService<ILocalEventBus>(); } @@ -288,4 +290,103 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule> }); entityUpdatedEventTriggered.ShouldBeFalse(); } + + [Fact] + public async Task Should_Trigger_EntityUpdatedEvent_For_Aggregate_Root_When_Property_And_Navigation_Changes_Tests() + { + var entityId = Guid.NewGuid(); + await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity")); + + var entityWithNavigationForeignId = Guid.NewGuid(); + var entityWithNavigationForeignId2 = Guid.NewGuid(); + await AppEntityWithNavigationForeignRepository.InsertAsync(new AppEntityWithNavigationsForeign(entityWithNavigationForeignId, "TestEntityWithNavigationForeign")); + await AppEntityWithNavigationForeignRepository.InsertAsync(new AppEntityWithNavigationsForeign(entityWithNavigationForeignId2, "TestEntityWithNavigationForeign2")); + + var entityUpdatedEventTriggered = false; + + LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigations>>(data => + { + entityUpdatedEventTriggered = !entityUpdatedEventTriggered; + return Task.CompletedTask; + }); + + // Test with simple property with foreign key + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.Name = Guid.NewGuid().ToString(); + entity.AppEntityWithNavigationForeignId = entityWithNavigationForeignId; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + + // Test only foreign key changed + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.AppEntityWithNavigationForeignId = entityWithNavigationForeignId2; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeFalse(); + + // Test with simple property with value object + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.Name = Guid.NewGuid().ToString(); + entity.AppEntityWithValueObjectAddress = new AppEntityWithValueObjectAddress("Turkey"); + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + + // Test with simple property with one to one + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.Name = Guid.NewGuid().ToString(); + entity.OneToOne = new AppEntityWithNavigationChildOneToOne + { + ChildName = "ChildName" + }; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + + // Test with simple property with one to many + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.Name = Guid.NewGuid().ToString(); + entity.OneToMany = new List<AppEntityWithNavigationChildOneToMany>() + { + new AppEntityWithNavigationChildOneToMany + { + AppEntityWithNavigationId = entity.Id, + ChildName = "ChildName1" + } + }; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + + // Test with simple property with many to many + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.Name = Guid.NewGuid().ToString(); + entity.ManyToMany = new List<AppEntityWithNavigationChildManyToMany>() + { + new AppEntityWithNavigationChildManyToMany + { + ChildName = "ChildName1" + } + }; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + } } diff --git a/latest-versions.json b/latest-versions.json index 67108ad57a..543e133787 100644 --- a/latest-versions.json +++ b/latest-versions.json @@ -1,6 +1,6 @@ [ { - "version": "8.1.1", + "version": "8.1.3", "releaseDate": "", "type": "stable", "message": "" diff --git a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs index f82f578da5..838e84f9ad 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs @@ -134,15 +134,29 @@ namespace Volo.Docs.Admin.Projects { throw new Exception("Cannot find the project with the Id " + projectId); } - - var docs = (await _documentRepository.GetListByProjectId(project.Id)) - .Where(doc => doc.FileName != project.NavigationDocumentName && doc.FileName != project.ParametersDocumentName) - .ToList(); + await _elasticSearchService.DeleteAllByProjectIdAsync(project.Id); - - if(docs.Any()) + + var docsCount = await _documentRepository.GetUniqueDocumentCountByProjectIdAsync(projectId); + + if (docsCount == 0) + { + return; + } + + const int maxResultCount = 1000; + + var skipCount = 0; + while(skipCount < docsCount) { - await _elasticSearchService.AddOrUpdateManyAsync(docs); + var docs = await _documentRepository.GetUniqueDocumentsByProjectIdPagedAsync(projectId, skipCount, maxResultCount); + docs = docs.Where(doc => doc.FileName != project.NavigationDocumentName && doc.FileName != project.ParametersDocumentName).ToList(); + if (!docs.Any()) + { + return; + } + await _elasticSearchService.AddOrUpdateManyAsync(docs); + skipCount += maxResultCount; } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs index c0a5b1295f..22d4bf9ff4 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs @@ -58,6 +58,26 @@ namespace Volo.Docs.Documents.FullSearch.Elastic public virtual async Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default) { var client = _clientProvider.GetClient(); + + // exist by name, project id, language code and version + var existResponse = await client.SearchAsync<EsDocument>(s => s + .Query(q => q + .Bool(b => b + .Must(m => m + .Term(t => t.ProjectId, NormalizeField(document.ProjectId)) + && m.Term(t => t.LanguageCode, NormalizeField(document.LanguageCode)) + && m.Term(t => t.Version, NormalizeField(document.Version)) + && m.Term(t => t.Name, document.Name) + ) + ) + ), cancellationToken); + + HandleError(existResponse); + + if (existResponse.Documents.Count != 0) + { + HandleError(await client.DeleteManyAsync(existResponse.Documents, _options.IndexName, cancellationToken)); + } var esDocument = new EsDocument { diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs index 7dc8f13147..86fb2cdc7f 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs @@ -13,6 +13,10 @@ namespace Volo.Docs.Documents Task<List<Document>> GetListByProjectId(Guid projectId, CancellationToken cancellationToken = default); + Task<List<Document>> GetUniqueDocumentsByProjectIdPagedAsync(Guid projectId, int skipCount, int maxResultCount, CancellationToken cancellationToken = default); + + Task<long> GetUniqueDocumentCountByProjectIdAsync(Guid projectId, CancellationToken cancellationToken = default); + Task UpdateProjectLastCachedTimeAsync(Guid projectId, DateTime cachedTime, CancellationToken cancellationToken = default); diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs index 1120e36d01..a585545bdf 100644 --- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs @@ -56,6 +56,27 @@ namespace Volo.Docs.Documents return await (await GetDbSetAsync()).Where(d => d.ProjectId == projectId).ToListAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task<List<Document>> GetUniqueDocumentsByProjectIdPagedAsync(Guid projectId, int skipCount, int maxResultCount, + CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(d => d.ProjectId == projectId) + .OrderBy(x => x.LastCachedTime) + .GroupBy(x => new { x.Name, x.LanguageCode, x.Version }) + .Select(group => group.First()) + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(cancellationToken); + } + + public virtual async Task<long> GetUniqueDocumentCountByProjectIdAsync(Guid projectId, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(d => d.ProjectId == projectId) + .GroupBy(x => new {x.FileName, x.Version, x.LanguageCode}) + .LongCountAsync(GetCancellationToken(cancellationToken)); + } + public async Task UpdateProjectLastCachedTimeAsync(Guid projectId, DateTime cachedTime, CancellationToken cancellationToken = default) { diff --git a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs index 9a3750deb0..c678947f5c 100644 --- a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs @@ -54,6 +54,26 @@ namespace Volo.Docs.Documents return await (await GetMongoQueryableAsync(cancellationToken)).Where(d => d.ProjectId == projectId).ToListAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task<List<Document>> GetUniqueDocumentsByProjectIdPagedAsync(Guid projectId, int skipCount, int maxResultCount, + CancellationToken cancellationToken = default) + { + return await (await GetMongoQueryableAsync(cancellationToken)) + .Where(d => d.ProjectId == projectId) + .OrderBy(x => x.LastCachedTime) + .GroupBy(x => new { x.Name, x.LanguageCode, x.Version }) + .Select(group => group.First()) + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(cancellationToken); + } + + public virtual async Task<long> GetUniqueDocumentCountByProjectIdAsync(Guid projectId, CancellationToken cancellationToken = default) + { + return await (await GetMongoQueryableAsync(cancellationToken)).Where(d => d.ProjectId == projectId) + .GroupBy(x => new { x.Name, x.LanguageCode, x.Version }) + .LongCountAsync(GetCancellationToken(cancellationToken)); + } + public async Task UpdateProjectLastCachedTimeAsync(Guid projectId, DateTime cachedTime, CancellationToken cancellationToken = default) { diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocumentRepository_Tests.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocumentRepository_Tests.cs index 742f80fe17..1c0c63bf64 100644 --- a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocumentRepository_Tests.cs +++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocumentRepository_Tests.cs @@ -43,5 +43,19 @@ namespace Volo.Docs var documentsAfterClear = await DocumentRepository.GetListByProjectId(DocsTestData.ProjectId); documentsAfterClear.ForEach(d => d.LastCachedTime.ShouldBe(DateTime.MinValue)); } + + [Fact] + public async Task GetUniqueDocumentsByProjectIdPagedAsync() + { + var documents = await DocumentRepository.GetUniqueDocumentsByProjectIdPagedAsync(DocsTestData.ProjectId, 0, 10); + documents.Count.ShouldBe(1); + } + + [Fact] + public async Task GetUniqueDocumentCountByProjectIdAsync() + { + var count = await DocumentRepository.GetUniqueDocumentCountByProjectIdAsync(DocsTestData.ProjectId); + count.ShouldBe(1); + } } } diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index f144639db4..707f1f846f 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -1,17 +1,21 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { CoreModule } from '@abp/ng.core'; +import { CoreModule, provideAbpCore, withOptions } from '@abp/ng.core'; import { registerLocale } from '@abp/ng.core/locale'; -import { InternetConnectionStatusComponent, ThemeSharedModule } from '@abp/ng.theme.shared'; +import { + InternetConnectionStatusComponent, + ThemeSharedModule, + provideAbpThemeShared, +} from '@abp/ng.theme.shared'; import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x'; import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts'; -import { IdentityConfigModule } from '@abp/ng.identity/config'; -import { AbpOAuthModule } from '@abp/ng.oauth'; -import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; -import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; -import { FeatureManagementModule } from '@abp/ng.feature-management'; -import { AccountConfigModule } from '@abp/ng.account/config'; +import { provideAbpOAuth } from '@abp/ng.oauth'; +import { provideSettingManagementConfig } from '@abp/ng.setting-management/config'; +import { provideAccountConfig } from '@abp/ng.account/config'; +import { provideIdentityConfig } from '@abp/ng.identity/config'; +import { provideTenantManagementConfig } from '@abp/ng.tenant-management/config'; +import { provideFeatureManagementConfig } from '@abp/ng.feature-management'; import { AccountLayoutModule } from '@abp/ng.theme.lepton-x/account'; import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; @@ -23,25 +27,31 @@ import { APP_ROUTE_PROVIDER } from './route.provider'; BrowserModule, BrowserAnimationsModule, AppRoutingModule, - CoreModule.forRoot({ - environment, - registerLocaleFn: registerLocale(), - sendNullsAsQueryParam: false, - skipGetAppConfiguration: false, - }), - AbpOAuthModule.forRoot(), - ThemeSharedModule.forRoot(), - AccountConfigModule.forRoot(), - IdentityConfigModule.forRoot(), - TenantManagementConfigModule.forRoot(), - FeatureManagementModule.forRoot(), - SettingManagementConfigModule.forRoot(), + CoreModule, + ThemeSharedModule, ThemeLeptonXModule.forRoot(), SideMenuLayoutModule.forRoot(), AccountLayoutModule.forRoot(), InternetConnectionStatusComponent, ], - providers: [APP_ROUTE_PROVIDER], + providers: [ + APP_ROUTE_PROVIDER, + provideAbpCore( + withOptions({ + environment, + registerLocaleFn: registerLocale(), + sendNullsAsQueryParam: false, + skipGetAppConfiguration: false, + }), + ), + provideAbpOAuth(), + provideAbpThemeShared(), + provideSettingManagementConfig(), + provideAccountConfig(), + provideIdentityConfig(), + provideTenantManagementConfig(), + provideFeatureManagementConfig(), + ], declarations: [AppComponent], bootstrap: [AppComponent], }) diff --git a/npm/ng-packs/packages/account/config/src/account-config.module.ts b/npm/ng-packs/packages/account/config/src/account-config.module.ts index 2c1345927b..97c38d2e56 100644 --- a/npm/ng-packs/packages/account/config/src/account-config.module.ts +++ b/npm/ng-packs/packages/account/config/src/account-config.module.ts @@ -1,21 +1,15 @@ -import { Injector, ModuleWithProviders, NgModule } from '@angular/core'; -import { NAVIGATE_TO_MANAGE_PROFILE } from '@abp/ng.core'; -import { ACCOUNT_ROUTE_PROVIDERS } from './providers/route.provider'; -import { navigateToManageProfileFactory } from './utils/factories'; +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { provideAccountConfig } from './providers'; +/** + * @deprecated AccountConfigModule is deprecated use `provideAccountConfig` *function* instead. + */ @NgModule() export class AccountConfigModule { static forRoot(): ModuleWithProviders<AccountConfigModule> { return { ngModule: AccountConfigModule, - providers: [ - ACCOUNT_ROUTE_PROVIDERS, - { - provide: NAVIGATE_TO_MANAGE_PROFILE, - useFactory: navigateToManageProfileFactory, - deps: [Injector], - }, - ], + providers: [provideAccountConfig()], }; } } diff --git a/npm/ng-packs/packages/account/config/src/providers/account-config.provider.ts b/npm/ng-packs/packages/account/config/src/providers/account-config.provider.ts new file mode 100644 index 0000000000..49304cdfc2 --- /dev/null +++ b/npm/ng-packs/packages/account/config/src/providers/account-config.provider.ts @@ -0,0 +1,15 @@ +import { NAVIGATE_TO_MANAGE_PROFILE } from '@abp/ng.core'; +import { makeEnvironmentProviders, Injector } from '@angular/core'; +import { navigateToManageProfileFactory } from '../utils'; +import { ACCOUNT_ROUTE_PROVIDERS } from './'; + +export function provideAccountConfig() { + return makeEnvironmentProviders([ + ACCOUNT_ROUTE_PROVIDERS, + { + provide: NAVIGATE_TO_MANAGE_PROFILE, + useFactory: navigateToManageProfileFactory, + deps: [Injector], + }, + ]); +} diff --git a/npm/ng-packs/packages/account/config/src/providers/index.ts b/npm/ng-packs/packages/account/config/src/providers/index.ts index fe08efba8c..e6961c188d 100644 --- a/npm/ng-packs/packages/account/config/src/providers/index.ts +++ b/npm/ng-packs/packages/account/config/src/providers/index.ts @@ -1 +1,2 @@ export * from './route.provider'; +export * from './account-config.provider'; diff --git a/npm/ng-packs/packages/account/config/src/public-api.ts b/npm/ng-packs/packages/account/config/src/public-api.ts index cb4f854a64..47a2c8d3d7 100644 --- a/npm/ng-packs/packages/account/config/src/public-api.ts +++ b/npm/ng-packs/packages/account/config/src/public-api.ts @@ -1,3 +1,4 @@ export * from './account-config.module'; export * from './enums'; export * from './providers'; +export * from './utils'; diff --git a/npm/ng-packs/packages/account/config/src/utils/index.ts b/npm/ng-packs/packages/account/config/src/utils/index.ts new file mode 100644 index 0000000000..3a94418e3f --- /dev/null +++ b/npm/ng-packs/packages/account/config/src/utils/index.ts @@ -0,0 +1 @@ +export * from './factories'; diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index eb475074ce..30c09c4c0c 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -1,8 +1,8 @@ import { CommonModule } from '@angular/common'; import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'; -import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angular/core'; +import { ModuleWithProviders, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { RouterModule, TitleStrategy } from '@angular/router'; +import { RouterModule } from '@angular/router'; import { AbstractNgModelComponent } from './abstracts/ng-model.component'; import { DynamicLayoutComponent } from './components/dynamic-layout.component'; import { ReplaceableRouteContainerComponent } from './components/replaceable-route-container.component'; @@ -15,36 +15,17 @@ import { InitDirective } from './directives/init.directive'; import { PermissionDirective } from './directives/permission.directive'; import { ReplaceableTemplateDirective } from './directives/replaceable-template.directive'; import { StopPropagationDirective } from './directives/stop-propagation.directive'; -import { RoutesHandler } from './handlers/routes.handler'; import { LocalizationModule } from './localization.module'; import { ABP } from './models/common'; import { LocalizationPipe } from './pipes/localization.pipe'; import { SortPipe } from './pipes/sort.pipe'; import { ToInjectorPipe } from './pipes/to-injector.pipe'; -import { CookieLanguageProvider } from './providers/cookie-language.provider'; -import { LocaleProvider } from './providers/locale.provider'; -import { LocalizationService } from './services/localization.service'; -import { OTHERS_GROUP } from './tokens'; -import { localizationContributor, LOCALIZATIONS } from './tokens/localization.token'; -import { CORE_OPTIONS, coreOptionsFactory } from './tokens/options.token'; -import { TENANT_KEY } from './tokens/tenant-key.token'; -import { noop } from './utils/common-utils'; import './utils/date-extensions'; -import { getInitialData, localeInitializer } from './utils/initial-utils'; import { ShortDateTimePipe } from './pipes/short-date-time.pipe'; import { ShortTimePipe } from './pipes/short-time.pipe'; import { ShortDatePipe } from './pipes/short-date.pipe'; import { SafeHtmlPipe } from './pipes/safe-html.pipe'; -import { QUEUE_MANAGER } from './tokens/queue.token'; -import { DefaultQueueManager } from './utils/queue'; -import { IncludeLocalizationResourcesProvider } from './providers/include-localization-resources.provider'; -import { SORT_COMPARE_FUNC, compareFuncFactory } from './tokens/compare-func.token'; -import { AuthErrorFilterService } from './abstracts'; -import { DYNAMIC_LAYOUTS_TOKEN } from "./tokens/dynamic-layout.token"; -import { DEFAULT_DYNAMIC_LAYOUTS } from "./constants"; -import { AbpTitleStrategy } from './services/title-strategy.service'; -import { LocalStorageListenerService } from './services/local-storage-listener.service'; - +import { provideAbpCoreChild, provideAbpCore, withOptions } from './providers'; const standaloneDirectives = [ AutofocusDirective, @@ -132,96 +113,23 @@ export class RootCoreModule {} imports: [BaseCoreModule], }) export class CoreModule { + /** + * @deprecated forRoot method is deprecated, use `provideAbpCore` *function* for config settings. + */ static forRoot(options = {} as ABP.Root): ModuleWithProviders<RootCoreModule> { return { ngModule: RootCoreModule, - providers: [ - LocaleProvider, - CookieLanguageProvider, - { - provide: 'CORE_OPTIONS', - useValue: options, - }, - { - provide: CORE_OPTIONS, - useFactory: coreOptionsFactory, - deps: ['CORE_OPTIONS'], - }, - { - provide: APP_INITIALIZER, - multi: true, - deps: [Injector], - useFactory: getInitialData, - }, - { - provide: APP_INITIALIZER, - multi: true, - deps: [Injector], - useFactory: localeInitializer, - }, - { - provide: APP_INITIALIZER, - multi: true, - deps: [LocalizationService], - useFactory: noop, - }, - { - provide: APP_INITIALIZER, - multi: true, - deps: [LocalStorageListenerService], - useFactory: noop, - }, - { - provide: APP_INITIALIZER, - multi: true, - deps: [RoutesHandler], - useFactory: noop, - }, - - { provide: TENANT_KEY, useValue: options.tenantKey || '__tenant' }, - { - provide: LOCALIZATIONS, - multi: true, - useValue: localizationContributor(options.localizations), - deps: [LocalizationService], - }, - { - provide: SORT_COMPARE_FUNC, - useFactory: compareFuncFactory, - }, - { - provide: QUEUE_MANAGER, - useClass: DefaultQueueManager, - }, - { - provide: OTHERS_GROUP, - useValue: options.othersGroup || 'AbpUi::OthersGroup', - }, - AuthErrorFilterService, - IncludeLocalizationResourcesProvider, - { - provide: DYNAMIC_LAYOUTS_TOKEN, - useValue: options.dynamicLayouts || DEFAULT_DYNAMIC_LAYOUTS - }, - { - provide: TitleStrategy, - useExisting: AbpTitleStrategy - } - ], + providers: [provideAbpCore(withOptions(options))], }; } + /** + * @deprecated forChild method is deprecated, use `provideAbpCoreChild` *function* for config settings. + */ static forChild(options = {} as ABP.Child): ModuleWithProviders<RootCoreModule> { return { ngModule: RootCoreModule, - providers: [ - { - provide: LOCALIZATIONS, - multi: true, - useValue: localizationContributor(options.localizations), - deps: [LocalizationService], - }, - ], + providers: [provideAbpCoreChild(options)], }; } } diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index 714f2422c2..704fde958a 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -15,6 +15,7 @@ export namespace ABP { localizations?: Localization[]; othersGroup?: string; dynamicLayouts?: Map<string, string>; + disableProjectNameInTitle?: boolean; } export interface Child { diff --git a/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts b/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts new file mode 100644 index 0000000000..b07697b135 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts @@ -0,0 +1,159 @@ +import { makeEnvironmentProviders, APP_INITIALIZER, Injector, Provider } from '@angular/core'; +import { TitleStrategy } from '@angular/router'; +import { + CORE_OPTIONS, + LOCALIZATIONS, + DYNAMIC_LAYOUTS_TOKEN, + OTHERS_GROUP, + QUEUE_MANAGER, + SORT_COMPARE_FUNC, + TENANT_KEY, + compareFuncFactory, + coreOptionsFactory, + localizationContributor, +} from '../tokens'; +import { RoutesHandler } from '../handlers'; +import { ABP, SortableItem } from '../models'; +import { AuthErrorFilterService } from '../abstracts'; +import { DEFAULT_DYNAMIC_LAYOUTS } from '../constants'; +import { LocalizationService, LocalStorageListenerService, AbpTitleStrategy } from '../services'; +import { DefaultQueueManager, getInitialData, localeInitializer, noop } from '../utils'; +import { CookieLanguageProvider, IncludeLocalizationResourcesProvider, LocaleProvider } from './'; + +export enum CoreFeatureKind { + Options, + CompareFunctionFactory, + TitleStrategy, +} + +export interface CoreFeature<KindT extends CoreFeatureKind> { + ɵkind: KindT; + ɵproviders: Provider[]; +} + +function makeCoreFeature<KindT extends CoreFeatureKind>( + kind: KindT, + providers: Provider[], +): CoreFeature<KindT> { + return { + ɵkind: kind, + ɵproviders: providers, + }; +} + +export function withOptions(options = {} as ABP.Root): CoreFeature<CoreFeatureKind.Options> { + return makeCoreFeature(CoreFeatureKind.Options, [ + { + provide: 'CORE_OPTIONS', + useValue: options, + }, + { + provide: CORE_OPTIONS, + useFactory: coreOptionsFactory, + deps: ['CORE_OPTIONS'], + }, + { provide: TENANT_KEY, useValue: options.tenantKey || '__tenant' }, + { + provide: LOCALIZATIONS, + multi: true, + useValue: localizationContributor(options.localizations), + deps: [LocalizationService], + }, + { + provide: OTHERS_GROUP, + useValue: options.othersGroup || 'AbpUi::OthersGroup', + }, + { + provide: DYNAMIC_LAYOUTS_TOKEN, + useValue: options.dynamicLayouts || DEFAULT_DYNAMIC_LAYOUTS, + }, + ]); +} + +export function withTitleStrategy(strategy: unknown): CoreFeature<CoreFeatureKind.TitleStrategy> { + return makeCoreFeature(CoreFeatureKind.TitleStrategy, [ + { + provide: TitleStrategy, + useExisting: strategy, + }, + ]); +} + +export function withCompareFuncFactory( + factory: (a: SortableItem, b: SortableItem) => 1 | -1 | 0, +): CoreFeature<CoreFeatureKind.CompareFunctionFactory> { + return makeCoreFeature(CoreFeatureKind.CompareFunctionFactory, [ + { + provide: SORT_COMPARE_FUNC, + useFactory: factory, + }, + ]); +} + +export function provideAbpCore(...features: CoreFeature<CoreFeatureKind>[]) { + const providers = [ + LocaleProvider, + CookieLanguageProvider, + { + provide: APP_INITIALIZER, + multi: true, + deps: [Injector], + useFactory: getInitialData, + }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [Injector], + useFactory: localeInitializer, + }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [LocalizationService], + useFactory: noop, + }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [LocalStorageListenerService], + useFactory: noop, + }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [RoutesHandler], + useFactory: noop, + }, + { + provide: SORT_COMPARE_FUNC, + useFactory: compareFuncFactory, + }, + { + provide: QUEUE_MANAGER, + useClass: DefaultQueueManager, + }, + AuthErrorFilterService, + IncludeLocalizationResourcesProvider, + { + provide: TitleStrategy, + useExisting: AbpTitleStrategy, + }, + ]; + + for (const feature of features) { + providers.push(...feature.ɵproviders); + } + + return makeEnvironmentProviders(providers); +} + +export function provideAbpCoreChild(options = {} as ABP.Child) { + return makeEnvironmentProviders([ + { + provide: LOCALIZATIONS, + multi: true, + useValue: localizationContributor(options.localizations), + deps: [LocalizationService], + }, + ]); +} diff --git a/npm/ng-packs/packages/core/src/lib/providers/index.ts b/npm/ng-packs/packages/core/src/lib/providers/index.ts index 4ae2d22a1a..c59b5d4618 100644 --- a/npm/ng-packs/packages/core/src/lib/providers/index.ts +++ b/npm/ng-packs/packages/core/src/lib/providers/index.ts @@ -1,3 +1,4 @@ export * from './cookie-language.provider'; export * from './locale.provider'; export * from './include-localization-resources.provider'; +export * from './core-module-config.provider'; diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index a20ae907ae..c47bef3106 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -22,3 +22,4 @@ export * from './local-storage.service'; export * from './window.service'; export * from './internet-connection-service'; export * from './local-storage-listener.service'; +export * from './title-strategy.service'; diff --git a/npm/ng-packs/packages/core/src/lib/services/title-strategy.service.ts b/npm/ng-packs/packages/core/src/lib/services/title-strategy.service.ts index 4cb351918d..af961794f8 100644 --- a/npm/ng-packs/packages/core/src/lib/services/title-strategy.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/title-strategy.service.ts @@ -2,21 +2,18 @@ import { Injectable, effect, inject } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { TitleStrategy, RouterStateSnapshot } from '@angular/router'; import { Title } from '@angular/platform-browser'; -import { ConfigStateService } from './config-state.service'; import { LocalizationService } from './localization.service'; +import { DISABLE_PROJECT_NAME } from '../tokens'; @Injectable({ providedIn: 'root', }) export class AbpTitleStrategy extends TitleStrategy { protected readonly title = inject(Title); - protected readonly configState = inject(ConfigStateService); protected readonly localizationService = inject(LocalizationService); + protected readonly disableProjectName = inject(DISABLE_PROJECT_NAME, { optional: true }); protected routerState: RouterStateSnapshot; - projectName = toSignal(this.configState.getDeep$('localization.defaultResourceName'), { - initialValue: 'MyProjectName', - }); langugageChange = toSignal(this.localizationService.languageChange$); constructor() { @@ -30,14 +27,22 @@ export class AbpTitleStrategy extends TitleStrategy { override updateTitle(routerState: RouterStateSnapshot) { this.routerState = routerState; - let title = this.buildTitle(routerState); + const title = this.buildTitle(routerState); + + const projectName = this.localizationService.instant({ + key: '::AppName', + defaultValue: 'MyProjectName', + }); if (!title) { - this.title.setTitle(this.projectName()); - return; + return this.title.setTitle(projectName); + } + + let localizedText = this.localizationService.instant({ key: title, defaultValue: title }); + if (!this.disableProjectName) { + localizedText += ` | ${projectName}`; } - const localizedTitle = this.localizationService.instant({ key: title, defaultValue: title }); - this.title.setTitle(`${localizedTitle} | ${this.projectName()}`); + this.title.setTitle(localizedText); } } diff --git a/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts index 8b2a99476a..68e47053fd 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts @@ -14,12 +14,11 @@ import { HttpErrorReporterService } from '../services/http-error-reporter.servic import { PermissionService } from '../services/permission.service'; import { RoutesService } from '../services/routes.service'; import { CORE_OPTIONS } from '../tokens/options.token'; -import { IncludeLocalizationResourcesProvider } from '../providers'; +import { IncludeLocalizationResourcesProvider, provideAbpCore, withOptions } from '../providers'; import { TestBed } from '@angular/core/testing'; import { RouterTestingHarness } from '@angular/router/testing'; import { OTHERS_GROUP } from '../tokens'; import { SORT_COMPARE_FUNC, compareFuncFactory } from '../tokens/compare-func.token'; -import { CoreModule } from '../core.module'; import { AuthService } from '../abstracts'; describe('PermissionGuard', () => { @@ -155,12 +154,13 @@ describe('authGuard', () => { permissionService = createSpyObject(PermissionService); TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, CoreModule.forRoot()], + imports: [HttpClientTestingModule], providers: [ { provide: AuthService, useValue: mockOAuthService }, { provide: PermissionService, useValue: permissionService }, { provide: HttpErrorReporterService, useValue: httpErrorReporter }, provideRouter(routes), + provideAbpCore(withOptions()), ], }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tokens/index.ts b/npm/ng-packs/packages/core/src/lib/tokens/index.ts index 7d0422ad7d..5ccb5160f0 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/index.ts @@ -14,4 +14,6 @@ export * from './check-authentication-state'; export * from './http-context.token'; export * from './others-group.token'; export * from './tenant-not-found-by-name'; -export * from './compare-func.token' +export * from './compare-func.token'; +export * from './dynamic-layout.token'; +export * from './title-strategy-disable-project-name.token'; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/title-strategy-disable-project-name.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/title-strategy-disable-project-name.token.ts new file mode 100644 index 0000000000..afbee19528 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tokens/title-strategy-disable-project-name.token.ts @@ -0,0 +1,3 @@ +import { InjectionToken } from '@angular/core'; + +export const DISABLE_PROJECT_NAME = new InjectionToken<boolean>('DISABLE_APP_NAME'); diff --git a/npm/ng-packs/packages/core/src/public-api.ts b/npm/ng-packs/packages/core/src/public-api.ts index c7b56ac41c..5511de5057 100644 --- a/npm/ng-packs/packages/core/src/public-api.ts +++ b/npm/ng-packs/packages/core/src/public-api.ts @@ -9,6 +9,7 @@ export * from './lib/guards'; export * from './lib/localization.module'; export * from './lib/models'; export * from './lib/pipes'; +export * from './lib/providers'; export * from './lib/proxy/pages/abp/multi-tenancy'; export * from './lib/proxy/volo/abp/asp-net-core/mvc/api-exploring'; export * from './lib/proxy/volo/abp/asp-net-core/mvc/application-configurations'; diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management-tab/feature-management-tab.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management-tab/feature-management-tab.component.ts index d65b3a74a9..4b367da436 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management-tab/feature-management-tab.component.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management-tab/feature-management-tab.component.ts @@ -1,8 +1,16 @@ +import { LocalizationModule, ReplaceableTemplateDirective } from '@abp/ng.core'; import { Component } from '@angular/core'; +import { FeatureManagementComponent } from '../feature-management/feature-management.component'; @Component({ + standalone: true, selector: 'abp-feature-management-tab', templateUrl: './feature-management-tab.component.html', + imports: [ + ReplaceableTemplateDirective, + LocalizationModule, + FeatureManagementComponent, + ], }) export class FeatureManagementTabComponent { visibleFeatures = false; diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts index 2dd2497e81..972daeb8c5 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts @@ -1,4 +1,4 @@ -import { ConfigStateService, TrackByService } from '@abp/ng.core'; +import { ConfigStateService, LocalizationModule, TrackByService } from '@abp/ng.core'; import { FeatureDto, FeatureGroupDto, @@ -9,10 +9,15 @@ import { Confirmation, ConfirmationService, LocaleDirection, + ThemeSharedModule, ToasterService, } from '@abp/ng.theme.shared'; import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; +import { NgTemplateOutlet } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { finalize } from 'rxjs/operators'; +import { FreeTextInputDirective } from '../../directives'; import { FeatureManagement } from '../../models/feature-management'; enum ValueTypes { @@ -22,9 +27,18 @@ enum ValueTypes { } @Component({ + standalone: true, selector: 'abp-feature-management', templateUrl: './feature-management.component.html', exportAs: 'abpFeatureManagement', + imports: [ + ThemeSharedModule, + LocalizationModule, + FormsModule, + NgbNavModule, + FreeTextInputDirective, + NgTemplateOutlet, + ], }) export class FeatureManagementComponent implements @@ -125,7 +139,7 @@ export class FeatureManagementComponent .subscribe(() => { this.visible = false; - this.toasterService.success('AbpFeatureManagement::Saved'); + this.toasterService.success('AbpUi::SavedSuccessfully'); if (!this.providerKey) { // to refresh host's features this.configState.refreshAppState().subscribe(); diff --git a/npm/ng-packs/packages/feature-management/src/lib/directives/free-text-input.directive.ts b/npm/ng-packs/packages/feature-management/src/lib/directives/free-text-input.directive.ts index 6b6556dcfa..750af65268 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/directives/free-text-input.directive.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/directives/free-text-input.directive.ts @@ -15,6 +15,7 @@ export const INPUT_TYPES = { }; @Directive({ + standalone: true, selector: 'input[abpFeatureManagementFreeText]', exportAs: 'inputAbpFeatureManagementFreeText', }) diff --git a/npm/ng-packs/packages/feature-management/src/lib/feature-management.module.ts b/npm/ng-packs/packages/feature-management/src/lib/feature-management.module.ts index 2de14108f1..fd3ab95386 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/feature-management.module.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/feature-management.module.ts @@ -4,7 +4,7 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { FeatureManagementComponent } from './components/feature-management/feature-management.component'; import { FreeTextInputDirective } from './directives/free-text-input.directive'; -import { FEATURE_MANAGEMENT_SETTINGS_PROVIDERS } from './providers'; +import { provideFeatureManagementConfig } from './providers'; import { FeatureManagementTabComponent } from './components'; const exported = [ @@ -13,16 +13,20 @@ const exported = [ FeatureManagementTabComponent, ]; +/** + * @deprecated FeatureManagementModule is deprecated . + * @description use `provideFeatureManagementConfig` *function* for config settings. + * You can import directives and pipes directly whichs were belongs to FeatureManagementModule are switched to standalone. + */ @NgModule({ - declarations: [...exported], - imports: [CoreModule, ThemeSharedModule, NgbNavModule], + imports: [CoreModule, ThemeSharedModule, NgbNavModule, ...exported], exports: [...exported], }) export class FeatureManagementModule { static forRoot(): ModuleWithProviders<FeatureManagementModule> { return { ngModule: FeatureManagementModule, - providers: [FEATURE_MANAGEMENT_SETTINGS_PROVIDERS], + providers: [provideFeatureManagementConfig()], }; } } diff --git a/npm/ng-packs/packages/feature-management/src/lib/providers/feature-management-config.provider.ts b/npm/ng-packs/packages/feature-management/src/lib/providers/feature-management-config.provider.ts new file mode 100644 index 0000000000..9df930a361 --- /dev/null +++ b/npm/ng-packs/packages/feature-management/src/lib/providers/feature-management-config.provider.ts @@ -0,0 +1,6 @@ +import { makeEnvironmentProviders } from '@angular/core'; +import { FEATURE_MANAGEMENT_SETTINGS_PROVIDERS } from './'; + +export function provideFeatureManagementConfig() { + return makeEnvironmentProviders([FEATURE_MANAGEMENT_SETTINGS_PROVIDERS]); +} diff --git a/npm/ng-packs/packages/feature-management/src/lib/providers/index.ts b/npm/ng-packs/packages/feature-management/src/lib/providers/index.ts index 34f4a30f78..fad7534e01 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/providers/index.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/providers/index.ts @@ -1 +1,2 @@ export * from './feature-management-settings.provider'; +export * from './feature-management-config.provider'; diff --git a/npm/ng-packs/packages/identity/config/src/identity-config.module.ts b/npm/ng-packs/packages/identity/config/src/identity-config.module.ts index 745578f69a..c6723775de 100644 --- a/npm/ng-packs/packages/identity/config/src/identity-config.module.ts +++ b/npm/ng-packs/packages/identity/config/src/identity-config.module.ts @@ -1,12 +1,15 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; -import { IDENTITY_ROUTE_PROVIDERS } from './providers/route.provider'; +import { provideIdentityConfig } from './providers'; +/** + * @deprecated IdentityConfigModule is deprecated use `provideIdentityConfig` *function* instead. + */ @NgModule() export class IdentityConfigModule { static forRoot(): ModuleWithProviders<IdentityConfigModule> { return { ngModule: IdentityConfigModule, - providers: [IDENTITY_ROUTE_PROVIDERS], + providers: [provideIdentityConfig()], }; } } diff --git a/npm/ng-packs/packages/identity/config/src/providers/identity-config.provider.ts b/npm/ng-packs/packages/identity/config/src/providers/identity-config.provider.ts new file mode 100644 index 0000000000..20a56dde0f --- /dev/null +++ b/npm/ng-packs/packages/identity/config/src/providers/identity-config.provider.ts @@ -0,0 +1,6 @@ +import { makeEnvironmentProviders } from '@angular/core'; +import { IDENTITY_ROUTE_PROVIDERS } from './'; + +export function provideIdentityConfig() { + return makeEnvironmentProviders([IDENTITY_ROUTE_PROVIDERS]); +} diff --git a/npm/ng-packs/packages/identity/config/src/providers/index.ts b/npm/ng-packs/packages/identity/config/src/providers/index.ts index fe08efba8c..1ae506684e 100644 --- a/npm/ng-packs/packages/identity/config/src/providers/index.ts +++ b/npm/ng-packs/packages/identity/config/src/providers/index.ts @@ -1 +1,2 @@ export * from './route.provider'; +export * from './identity-config.provider'; diff --git a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts index 2adac2a23a..35cfa769f3 100644 --- a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts +++ b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts @@ -1,73 +1,15 @@ -import { APP_INITIALIZER, ModuleWithProviders, NgModule, Provider } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; -import { - AbpLocalStorageService, - ApiInterceptor, - AuthErrorFilterService, - AuthGuard, - authGuard, - AuthService, - CHECK_AUTHENTICATION_STATE_FN_KEY, - noop, - PIPE_TO_LOGIN_FN_KEY, -} from '@abp/ng.core'; -import { AbpOAuthService, OAuthErrorFilterService } from './services'; -import { OAuthConfigurationHandler } from './handlers/oauth-configuration.handler'; -import { OAuthApiInterceptor } from './interceptors/api.interceptor'; -import { AbpOAuthGuard, abpOAuthGuard } from './guards/oauth.guard'; -import { NavigateToManageProfileProvider } from './providers'; -import { checkAccessToken, pipeToLogin } from './utils'; +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { provideAbpOAuth } from './providers'; -@NgModule({ - imports: [CommonModule, OAuthModule], -}) +/** + * @deprecated AbpOAuthModule is deprecated use `provideAbpOAuth` *function* instead. + */ +@NgModule() export class AbpOAuthModule { static forRoot(): ModuleWithProviders<AbpOAuthModule> { return { ngModule: AbpOAuthModule, - providers: [ - { - provide: AuthService, - useClass: AbpOAuthService, - }, - { - provide: AuthGuard, - useClass: AbpOAuthGuard, - }, - { - provide: authGuard, - useValue: abpOAuthGuard, - }, - { - provide: ApiInterceptor, - useClass: OAuthApiInterceptor, - }, - { - provide: PIPE_TO_LOGIN_FN_KEY, - useValue: pipeToLogin, - }, - { - provide: CHECK_AUTHENTICATION_STATE_FN_KEY, - useValue: checkAccessToken, - }, - { - provide: HTTP_INTERCEPTORS, - useExisting: ApiInterceptor, - multi: true, - }, - NavigateToManageProfileProvider, - { - provide: APP_INITIALIZER, - multi: true, - deps: [OAuthConfigurationHandler], - useFactory: noop, - }, - OAuthModule.forRoot().providers as Provider[], - { provide: OAuthStorage, useClass: AbpLocalStorageService }, - { provide: AuthErrorFilterService, useExisting: OAuthErrorFilterService }, - ], + providers: [provideAbpOAuth()], }; } } diff --git a/npm/ng-packs/packages/oauth/src/lib/providers/index.ts b/npm/ng-packs/packages/oauth/src/lib/providers/index.ts index d938907916..abe17a431f 100644 --- a/npm/ng-packs/packages/oauth/src/lib/providers/index.ts +++ b/npm/ng-packs/packages/oauth/src/lib/providers/index.ts @@ -1 +1,2 @@ export * from './navigate-to-manage-profile.provider'; +export * from './oauth-module-config.provider'; diff --git a/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts b/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts new file mode 100644 index 0000000000..021ad59a05 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts @@ -0,0 +1,66 @@ +import { + AuthService, + AuthGuard, + authGuard, + ApiInterceptor, + PIPE_TO_LOGIN_FN_KEY, + CHECK_AUTHENTICATION_STATE_FN_KEY, + AbpLocalStorageService, + AuthErrorFilterService, + noop, +} from '@abp/ng.core'; +import { APP_INITIALIZER, Provider, makeEnvironmentProviders } from '@angular/core'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; +import { AbpOAuthGuard, abpOAuthGuard } from '../guards'; +import { OAuthConfigurationHandler } from '../handlers'; +import { OAuthApiInterceptor } from '../interceptors'; +import { AbpOAuthService, OAuthErrorFilterService } from '../services'; +import { pipeToLogin, checkAccessToken } from '../utils'; +import { NavigateToManageProfileProvider } from './navigate-to-manage-profile.provider'; + +export function provideAbpOAuth() { + const providers = [ + { + provide: AuthService, + useClass: AbpOAuthService, + }, + { + provide: AuthGuard, + useClass: AbpOAuthGuard, + }, + { + provide: authGuard, + useValue: abpOAuthGuard, + }, + { + provide: ApiInterceptor, + useClass: OAuthApiInterceptor, + }, + { + provide: PIPE_TO_LOGIN_FN_KEY, + useValue: pipeToLogin, + }, + { + provide: CHECK_AUTHENTICATION_STATE_FN_KEY, + useValue: checkAccessToken, + }, + { + provide: HTTP_INTERCEPTORS, + useExisting: ApiInterceptor, + multi: true, + }, + NavigateToManageProfileProvider, + { + provide: APP_INITIALIZER, + multi: true, + deps: [OAuthConfigurationHandler], + useFactory: noop, + }, + OAuthModule.forRoot().providers as Provider[], + { provide: OAuthStorage, useClass: AbpLocalStorageService }, + { provide: AuthErrorFilterService, useExisting: OAuthErrorFilterService }, + ]; + + return makeEnvironmentProviders(providers); +} diff --git a/npm/ng-packs/packages/setting-management/config/src/lib/providers/index.ts b/npm/ng-packs/packages/setting-management/config/src/lib/providers/index.ts index b44902e5d5..c0c77b7214 100644 --- a/npm/ng-packs/packages/setting-management/config/src/lib/providers/index.ts +++ b/npm/ng-packs/packages/setting-management/config/src/lib/providers/index.ts @@ -1,3 +1,5 @@ export * from './route.provider'; export * from './setting-tab.provider'; export * from './visible.provider'; +export * from './setting-management-config.provider'; +export * from './features.token'; diff --git a/npm/ng-packs/packages/setting-management/config/src/lib/providers/setting-management-config.provider.ts b/npm/ng-packs/packages/setting-management/config/src/lib/providers/setting-management-config.provider.ts new file mode 100644 index 0000000000..b981bc5147 --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/src/lib/providers/setting-management-config.provider.ts @@ -0,0 +1,16 @@ +import { makeEnvironmentProviders } from '@angular/core'; +import { + SETTING_MANAGEMENT_FEATURES_PROVIDERS, + SETTING_MANAGEMENT_ROUTE_PROVIDERS, + SETTING_MANAGEMENT_SETTING_TAB_PROVIDERS, + SETTING_MANAGEMENT_VISIBLE_PROVIDERS, +} from './'; + +export function provideSettingManagementConfig() { + return makeEnvironmentProviders([ + SETTING_MANAGEMENT_ROUTE_PROVIDERS, + SETTING_MANAGEMENT_SETTING_TAB_PROVIDERS, + SETTING_MANAGEMENT_FEATURES_PROVIDERS, + SETTING_MANAGEMENT_VISIBLE_PROVIDERS, + ]); +} diff --git a/npm/ng-packs/packages/setting-management/config/src/lib/setting-management-config.module.ts b/npm/ng-packs/packages/setting-management/config/src/lib/setting-management-config.module.ts index 60f567c31a..45b2340feb 100644 --- a/npm/ng-packs/packages/setting-management/config/src/lib/setting-management-config.module.ts +++ b/npm/ng-packs/packages/setting-management/config/src/lib/setting-management-config.module.ts @@ -2,27 +2,22 @@ import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { ModuleWithProviders, NgModule } from '@angular/core'; import { CoreModule } from '@abp/ng.core'; import { NgxValidateCoreModule } from '@ngx-validate/core'; -import { SETTING_MANAGEMENT_FEATURES_PROVIDERS } from './providers/features.token'; -import { SETTING_MANAGEMENT_VISIBLE_PROVIDERS } from './providers/visible.provider'; -import { SETTING_MANAGEMENT_ROUTE_PROVIDERS } from './providers/route.provider'; -import { SETTING_MANAGEMENT_SETTING_TAB_PROVIDERS } from './providers/setting-tab.provider'; import { EmailSettingGroupComponent } from './components/email-setting-group/email-setting-group.component'; +import { provideSettingManagementConfig } from './providers'; @NgModule({ - imports: [CoreModule,ThemeSharedModule,NgxValidateCoreModule], + imports: [CoreModule, ThemeSharedModule, NgxValidateCoreModule], declarations: [EmailSettingGroupComponent], exports: [EmailSettingGroupComponent], }) export class SettingManagementConfigModule { + /** + * @deprecated forRoot method is deprecated, use `provideSettingManagementConfig` *function* for config settings. + */ static forRoot(): ModuleWithProviders<SettingManagementConfigModule> { return { ngModule: SettingManagementConfigModule, - providers: [ - SETTING_MANAGEMENT_ROUTE_PROVIDERS, - SETTING_MANAGEMENT_SETTING_TAB_PROVIDERS, - SETTING_MANAGEMENT_FEATURES_PROVIDERS, - SETTING_MANAGEMENT_VISIBLE_PROVIDERS, - ], + providers: [provideSettingManagementConfig()], }; } } diff --git a/npm/ng-packs/packages/tenant-management/config/src/providers/index.ts b/npm/ng-packs/packages/tenant-management/config/src/providers/index.ts index fe08efba8c..39ed6c50ee 100644 --- a/npm/ng-packs/packages/tenant-management/config/src/providers/index.ts +++ b/npm/ng-packs/packages/tenant-management/config/src/providers/index.ts @@ -1 +1,2 @@ export * from './route.provider'; +export * from './tenant-management-config.provider'; diff --git a/npm/ng-packs/packages/tenant-management/config/src/providers/tenant-management-config.provider.ts b/npm/ng-packs/packages/tenant-management/config/src/providers/tenant-management-config.provider.ts new file mode 100644 index 0000000000..c5599e410c --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/src/providers/tenant-management-config.provider.ts @@ -0,0 +1,6 @@ +import { makeEnvironmentProviders } from '@angular/core'; +import { TENANT_MANAGEMENT_ROUTE_PROVIDERS } from './'; + +export function provideTenantManagementConfig() { + return makeEnvironmentProviders([TENANT_MANAGEMENT_ROUTE_PROVIDERS]); +} diff --git a/npm/ng-packs/packages/tenant-management/config/src/tenant-management-config.module.ts b/npm/ng-packs/packages/tenant-management/config/src/tenant-management-config.module.ts index 2929f766c0..3e93ac6bb8 100644 --- a/npm/ng-packs/packages/tenant-management/config/src/tenant-management-config.module.ts +++ b/npm/ng-packs/packages/tenant-management/config/src/tenant-management-config.module.ts @@ -1,12 +1,15 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; -import { TENANT_MANAGEMENT_ROUTE_PROVIDERS } from './providers/route.provider'; +import { provideTenantManagementConfig } from './providers'; +/** + * @deprecated TenantManagementConfigModule is deprecated use `provideTenantManagementConfig` *function* instead. + */ @NgModule() export class TenantManagementConfigModule { static forRoot(): ModuleWithProviders<TenantManagementConfigModule> { return { ngModule: TenantManagementConfigModule, - providers: [TENANT_MANAGEMENT_ROUTE_PROVIDERS], + providers: [provideTenantManagementConfig()], }; } } diff --git a/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts b/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts index 4b44814bce..76c917c51b 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts @@ -1,5 +1,4 @@ import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; -import { FeatureManagementModule } from '@abp/ng.feature-management'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { ExtensibleModule } from '@abp/ng.components/extensible'; import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; @@ -17,6 +16,7 @@ import { TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS, } from './tokens/extensions.token'; import { PageModule } from '@abp/ng.components/page'; +import { FeatureManagementComponent } from '@abp/ng.feature-management'; @NgModule({ declarations: [TenantsComponent], @@ -27,9 +27,9 @@ import { PageModule } from '@abp/ng.components/page'; CoreModule, ThemeSharedModule, NgbDropdownModule, - FeatureManagementModule, ExtensibleModule, PageModule, + FeatureManagementComponent, ], }) export class TenantManagementModule { diff --git a/npm/ng-packs/packages/theme-basic/src/lib/providers/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/providers/index.ts index d3028eab68..d1dc780142 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/providers/index.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/providers/index.ts @@ -1,3 +1,4 @@ export * from './nav-item.provider'; export * from './styles.provider'; export * from './user-menu.provider'; +export * from './theme-basic-config.provider'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/providers/theme-basic-config.provider.ts b/npm/ng-packs/packages/theme-basic/src/lib/providers/theme-basic-config.provider.ts new file mode 100644 index 0000000000..c75b5b0b6d --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/providers/theme-basic-config.provider.ts @@ -0,0 +1,39 @@ +import { APP_INITIALIZER, makeEnvironmentProviders } from '@angular/core'; +import { noop } from '@abp/ng.core'; +import { + VALIDATION_ERROR_TEMPLATE, + VALIDATION_TARGET_SELECTOR, + VALIDATION_INVALID_CLASSES, +} from '@ngx-validate/core'; +import { LazyStyleHandler } from '../handlers'; +import { BASIC_THEME_NAV_ITEM_PROVIDERS } from './nav-item.provider'; +import { BASIC_THEME_STYLES_PROVIDERS } from './styles.provider'; +import { BASIC_THEME_USER_MENU_PROVIDERS } from './user-menu.provider'; +import { ValidationErrorComponent } from '../components'; + +export function provideThemeBasicConfig() { + return makeEnvironmentProviders([ + BASIC_THEME_NAV_ITEM_PROVIDERS, + BASIC_THEME_USER_MENU_PROVIDERS, + BASIC_THEME_STYLES_PROVIDERS, + { + provide: VALIDATION_ERROR_TEMPLATE, + useValue: ValidationErrorComponent, + }, + { + provide: VALIDATION_TARGET_SELECTOR, + useValue: '.form-group', + }, + { + provide: VALIDATION_INVALID_CLASSES, + useValue: 'is-invalid', + }, + LazyStyleHandler, + { + provide: APP_INITIALIZER, + useFactory: noop, + multi: true, + deps: [LazyStyleHandler], + }, + ]); +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts index e04ed6dde1..d959509dfd 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts @@ -1,13 +1,8 @@ -import { CoreModule, noop } from '@abp/ng.core'; +import { CoreModule } from '@abp/ng.core'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; -import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core'; +import { ModuleWithProviders, NgModule } from '@angular/core'; import { NgbCollapseModule, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; -import { - NgxValidateCoreModule, - VALIDATION_ERROR_TEMPLATE, - VALIDATION_INVALID_CLASSES, - VALIDATION_TARGET_SELECTOR, -} from '@ngx-validate/core'; +import { NgxValidateCoreModule } from '@ngx-validate/core'; import { AccountLayoutComponent } from './components/account-layout/account-layout.component'; import { AuthWrapperComponent } from './components/account-layout/auth-wrapper/auth-wrapper.component'; import { TenantBoxComponent } from './components/account-layout/tenant-box/tenant-box.component'; @@ -20,10 +15,7 @@ import { NavItemsComponent } from './components/nav-items/nav-items.component'; import { PageAlertContainerComponent } from './components/page-alert-container/page-alert-container.component'; import { RoutesComponent } from './components/routes/routes.component'; import { ValidationErrorComponent } from './components/validation-error/validation-error.component'; -import { LazyStyleHandler } from './handlers/lazy-style.handler'; -import { BASIC_THEME_NAV_ITEM_PROVIDERS } from './providers/nav-item.provider'; -import { BASIC_THEME_STYLES_PROVIDERS } from './providers/styles.provider'; -import { BASIC_THEME_USER_MENU_PROVIDERS } from './providers/user-menu.provider'; +import { provideThemeBasicConfig } from './providers'; export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, EmptyLayoutComponent]; @@ -65,33 +57,13 @@ export class BaseThemeBasicModule {} imports: [BaseThemeBasicModule], }) export class ThemeBasicModule { + /** + * @deprecated forRoot method is deprecated, use `provideThemeBasicConfig` *function* for config settings. + */ static forRoot(): ModuleWithProviders<ThemeBasicModule> { return { ngModule: ThemeBasicModule, - providers: [ - BASIC_THEME_NAV_ITEM_PROVIDERS, - BASIC_THEME_USER_MENU_PROVIDERS, - BASIC_THEME_STYLES_PROVIDERS, - { - provide: VALIDATION_ERROR_TEMPLATE, - useValue: ValidationErrorComponent, - }, - { - provide: VALIDATION_TARGET_SELECTOR, - useValue: '.form-group', - }, - { - provide: VALIDATION_INVALID_CLASSES, - useValue: 'is-invalid', - }, - LazyStyleHandler, - { - provide: APP_INITIALIZER, - useFactory: noop, - multi: true, - deps: [LazyStyleHandler], - }, - ], + providers: [provideThemeBasicConfig()], }; } } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/providers/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/providers/index.ts index b6b1f9b60f..cf462deae8 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/providers/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/providers/index.ts @@ -2,3 +2,4 @@ export * from './ng-bootstrap-config.provider'; export * from './route.provider'; export * from './tenant-not-found.provider'; export * from './error-handlers.provider'; +export * from './theme-shared-config.provider'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/providers/theme-shared-config.provider.ts b/npm/ng-packs/packages/theme-shared/src/lib/providers/theme-shared-config.provider.ts new file mode 100644 index 0000000000..ad29327e18 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/providers/theme-shared-config.provider.ts @@ -0,0 +1,161 @@ +import { APP_INITIALIZER, Provider, makeEnvironmentProviders } from '@angular/core'; +import { noop } from '@abp/ng.core'; +import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap'; +import { + VALIDATION_BLUEPRINTS, + VALIDATION_MAP_ERRORS_FN, + defaultMapErrorsFn, + VALIDATION_VALIDATE_ON_SUBMIT, + Validation, +} from '@ngx-validate/core'; +import { DEFAULT_VALIDATION_BLUEPRINTS } from '../constants'; +import { DocumentDirHandlerService, ErrorHandler } from '../handlers'; +import { HttpErrorConfig } from '../models'; +import { + THEME_SHARED_APPEND_CONTENT, + HTTP_ERROR_CONFIG, + CONFIRMATION_ICONS, + ConfirmationIcons, + DEFAULT_CONFIRMATION_ICONS, +} from '../tokens'; +import { DateParserFormatter } from '../utils'; +import { + DEFAULT_HANDLERS_PROVIDERS, + NG_BOOTSTRAP_CONFIG_PROVIDERS, + THEME_SHARED_ROUTE_PROVIDERS, + tenantNotFoundProvider, +} from './'; + +export enum ThemeSharedFeatureKind { + HttpErrorConfig, + ValidationBluePrint, + ValidationErrorsFn, + ValidateOnSubmit, + Validation, + ConfirmationIcons, +} + +export interface ThemeSharedFeature<KindT extends ThemeSharedFeatureKind> { + ɵkind: KindT; + ɵproviders: Provider[]; +} + +function makeThemeSharedFeature<KindT extends ThemeSharedFeatureKind>( + kind: KindT, + providers: Provider[], +): ThemeSharedFeature<KindT> { + return { + ɵkind: kind, + ɵproviders: providers, + }; +} + +export function withHttpErrorConfig( + httpErrorConfig: HttpErrorConfig, +): ThemeSharedFeature<ThemeSharedFeatureKind.HttpErrorConfig> { + return makeThemeSharedFeature(ThemeSharedFeatureKind.HttpErrorConfig, [ + { + provide: HTTP_ERROR_CONFIG, + useValue: httpErrorConfig, + }, + ]); +} + +export function withValidationBluePrint( + bluePrints: Validation.Blueprints, +): ThemeSharedFeature<ThemeSharedFeatureKind.ValidationBluePrint> { + return makeThemeSharedFeature(ThemeSharedFeatureKind.ValidationBluePrint, [ + { + provide: VALIDATION_BLUEPRINTS, + useValue: { + ...DEFAULT_VALIDATION_BLUEPRINTS, + ...(bluePrints || {}), + }, + }, + ]); +} + +export function withValidationMapErrorsFn( + mapErrorsFn: Validation.MapErrorsFn, +): ThemeSharedFeature<ThemeSharedFeatureKind.ValidationErrorsFn> { + return makeThemeSharedFeature(ThemeSharedFeatureKind.ValidationErrorsFn, [ + { + provide: VALIDATION_MAP_ERRORS_FN, + useValue: mapErrorsFn || defaultMapErrorsFn, + }, + ]); +} + +export function withValidateOnSubmit( + validateOnSubmit: boolean, +): ThemeSharedFeature<ThemeSharedFeatureKind.ValidateOnSubmit> { + return makeThemeSharedFeature(ThemeSharedFeatureKind.ValidateOnSubmit, [ + { + provide: VALIDATION_VALIDATE_ON_SUBMIT, + useValue: validateOnSubmit, + }, + ]); +} + +export function withConfirmationIcon( + confirmationIcons: Partial<ConfirmationIcons>, +): ThemeSharedFeature<ThemeSharedFeatureKind.HttpErrorConfig> { + return makeThemeSharedFeature(ThemeSharedFeatureKind.HttpErrorConfig, [ + { + provide: CONFIRMATION_ICONS, + useValue: { ...DEFAULT_CONFIRMATION_ICONS, ...(confirmationIcons || {}) }, + }, + ]); +} + +export function provideAbpThemeShared(...features: ThemeSharedFeature<ThemeSharedFeatureKind>[]) { + const providers = [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [ErrorHandler], + useFactory: noop, + }, + THEME_SHARED_ROUTE_PROVIDERS, + { + provide: APP_INITIALIZER, + multi: true, + deps: [THEME_SHARED_APPEND_CONTENT], + useFactory: noop, + }, + { provide: HTTP_ERROR_CONFIG, useValue: undefined }, + { provide: NgbDateParserFormatter, useClass: DateParserFormatter }, + NG_BOOTSTRAP_CONFIG_PROVIDERS, + { + provide: VALIDATION_BLUEPRINTS, + useValue: { ...DEFAULT_VALIDATION_BLUEPRINTS }, + }, + { + provide: VALIDATION_MAP_ERRORS_FN, + useValue: defaultMapErrorsFn, + }, + { + provide: VALIDATION_VALIDATE_ON_SUBMIT, + useValue: undefined, + }, + DocumentDirHandlerService, + { + provide: APP_INITIALIZER, + useFactory: noop, + multi: true, + deps: [DocumentDirHandlerService], + }, + { + provide: CONFIRMATION_ICONS, + useValue: { ...DEFAULT_CONFIRMATION_ICONS }, + }, + tenantNotFoundProvider, + DEFAULT_HANDLERS_PROVIDERS, + ]; + + for (const feature of features) { + providers.push(...feature.ɵproviders); + } + + return makeEnvironmentProviders(providers); +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index 4b092642c1..50d179c3a9 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -1,14 +1,8 @@ -import { CoreModule, noop } from '@abp/ng.core'; +import { CoreModule } from '@abp/ng.core'; import { DatePipe } from '@angular/common'; -import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core'; -import { NgbDateParserFormatter, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'; -import { - defaultMapErrorsFn, - NgxValidateCoreModule, - VALIDATION_BLUEPRINTS, - VALIDATION_MAP_ERRORS_FN, - VALIDATION_VALIDATE_ON_SUBMIT, -} from '@ngx-validate/core'; +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgxValidateCoreModule } from '@ngx-validate/core'; import { NgxDatatableModule } from '@swimlane/ngx-datatable'; import { BreadcrumbItemsComponent } from './components/breadcrumb-items/breadcrumb-items.component'; import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component'; @@ -21,26 +15,24 @@ import { ModalCloseDirective } from './components/modal/modal-close.directive'; import { ModalComponent } from './components/modal/modal.component'; import { ToastContainerComponent } from './components/toast-container/toast-container.component'; import { ToastComponent } from './components/toast/toast.component'; -import { DEFAULT_VALIDATION_BLUEPRINTS } from './constants/validation'; import { EllipsisDirective } from './directives/ellipsis.directive'; import { LoadingDirective } from './directives/loading.directive'; import { NgxDatatableDefaultDirective } from './directives/ngx-datatable-default.directive'; import { NgxDatatableListDirective } from './directives/ngx-datatable-list.directive'; -import { DocumentDirHandlerService } from './handlers/document-dir.handler'; -import { ErrorHandler } from './handlers/error.handler'; import { RootParams } from './models/common'; -import { DEFAULT_HANDLERS_PROVIDERS, NG_BOOTSTRAP_CONFIG_PROVIDERS } from './providers'; -import { THEME_SHARED_ROUTE_PROVIDERS } from './providers/route.provider'; -import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token'; -import { HTTP_ERROR_CONFIG } from './tokens/http-error.token'; -import { DateParserFormatter } from './utils/date-parser-formatter'; -import { CONFIRMATION_ICONS, DEFAULT_CONFIRMATION_ICONS } from './tokens/confirmation-icons.token'; +import { + provideAbpThemeShared, + withConfirmationIcon, + withHttpErrorConfig, + withValidateOnSubmit, + withValidationBluePrint, + withValidationMapErrorsFn, +} from './providers'; import { PasswordComponent } from './components/password/password.component'; import { CardModule } from './components/card/card.module'; import { AbpVisibleDirective, DisabledDirective } from './directives'; import { FormInputComponent } from './components/form-input/form-input.component'; import { FormCheckboxComponent } from './components/checkbox/checkbox.component'; -import { tenantNotFoundProvider } from './providers/tenant-not-found.provider'; const declarationsWithExports = [ BreadcrumbComponent, @@ -93,59 +85,22 @@ export class BaseThemeSharedModule {} exports: [BaseThemeSharedModule], }) export class ThemeSharedModule { + /** + * @deprecated forRoot method is deprecated, use `provideAbpThemeShared` *function* for config settings. + */ static forRoot( { httpErrorConfig, validation = {}, confirmationIcons = {} } = {} as RootParams, ): ModuleWithProviders<ThemeSharedModule> { return { ngModule: ThemeSharedModule, providers: [ - { - provide: APP_INITIALIZER, - multi: true, - deps: [ErrorHandler], - useFactory: noop, - }, - THEME_SHARED_ROUTE_PROVIDERS, - { - provide: APP_INITIALIZER, - multi: true, - deps: [THEME_SHARED_APPEND_CONTENT], - useFactory: noop, - }, - { provide: HTTP_ERROR_CONFIG, useValue: httpErrorConfig }, - { provide: NgbDateParserFormatter, useClass: DateParserFormatter }, - NG_BOOTSTRAP_CONFIG_PROVIDERS, - { - provide: VALIDATION_BLUEPRINTS, - useValue: { - ...DEFAULT_VALIDATION_BLUEPRINTS, - ...(validation.blueprints || {}), - }, - }, - { - provide: VALIDATION_MAP_ERRORS_FN, - useValue: validation.mapErrorsFn || defaultMapErrorsFn, - }, - { - provide: VALIDATION_VALIDATE_ON_SUBMIT, - useValue: validation.validateOnSubmit, - }, - DocumentDirHandlerService, - { - provide: APP_INITIALIZER, - useFactory: noop, - multi: true, - deps: [DocumentDirHandlerService], - }, - { - provide: CONFIRMATION_ICONS, - useValue: { - ...DEFAULT_CONFIRMATION_ICONS, - ...(confirmationIcons || {}), - }, - }, - tenantNotFoundProvider, - DEFAULT_HANDLERS_PROVIDERS, + provideAbpThemeShared( + withHttpErrorConfig(httpErrorConfig), + withValidationBluePrint(validation.blueprints), + withValidationMapErrorsFn(validation.mapErrorsFn), + withValidateOnSubmit(validation.validateOnSubmit), + withConfirmationIcon(confirmationIcons), + ), ], }; } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts index 98edb1a87b..b38eb7ae3e 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts @@ -2,3 +2,4 @@ export * from './append-content.token'; export * from './http-error.token'; export * from './ngx-datatable-messages.token'; export * from './suppress-unsaved-changes-warning.token'; +export * from './confirmation-icons.token'; diff --git a/npm/packs/datatables.net/abp.resourcemapping.js b/npm/packs/datatables.net/abp.resourcemapping.js index 53561d1e3e..4c5147ec27 100644 --- a/npm/packs/datatables.net/abp.resourcemapping.js +++ b/npm/packs/datatables.net/abp.resourcemapping.js @@ -1,5 +1,6 @@ module.exports = { mappings: { - "@node_modules/datatables.net/js/jquery.dataTables.js": "@libs/datatables.net/js/" + "@node_modules/datatables.net/js/dataTables.min.js": "@libs/datatables.net/js/", + "@node_modules/datatables.net/js/jquery.dataTables.js": "@libs/datatables.net/js/" //TODO: Compatible code, Remove it after 8.3 rc1. } } \ No newline at end of file diff --git a/templates/app-nolayers/angular/src/app/app.module.ts b/templates/app-nolayers/angular/src/app/app.module.ts index 76e5c5ddcc..48ae3ab98a 100644 --- a/templates/app-nolayers/angular/src/app/app.module.ts +++ b/templates/app-nolayers/angular/src/app/app.module.ts @@ -1,12 +1,18 @@ -import { AccountConfigModule } from '@abp/ng.account/config'; -import { CoreModule } from '@abp/ng.core'; +import { provideAccountConfig } from '@abp/ng.account/config'; +import { CoreModule, provideAbpCore, withOptions } from '@abp/ng.core'; import { registerLocale } from '@abp/ng.core/locale'; -import { IdentityConfigModule } from '@abp/ng.identity/config'; -import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; -import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; +import { provideIdentityConfig } from '@abp/ng.identity/config'; +import { provideSettingManagementConfig } from '@abp/ng.setting-management/config'; +import { provideTenantManagementConfig } from '@abp/ng.tenant-management/config'; import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x'; import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts'; -import { InternetConnectionStatusComponent, ThemeSharedModule } from '@abp/ng.theme.shared'; +import { + InternetConnectionStatusComponent, + ThemeSharedModule, + provideAbpThemeShared, +} from '@abp/ng.theme.shared'; +import { provideFeatureManagementConfig } from '@abp/ng.feature-management'; +import { provideAbpOAuth } from '@abp/ng.oauth'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -14,31 +20,35 @@ import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { APP_ROUTE_PROVIDER } from './route.provider'; -import { FeatureManagementModule } from '@abp/ng.feature-management'; -import { AbpOAuthModule } from '@abp/ng.oauth'; @NgModule({ imports: [ BrowserModule, BrowserAnimationsModule, AppRoutingModule, - CoreModule.forRoot({ - environment, - registerLocaleFn: registerLocale(), - }), - AbpOAuthModule.forRoot(), - ThemeSharedModule.forRoot(), - AccountConfigModule.forRoot(), - IdentityConfigModule.forRoot(), - TenantManagementConfigModule.forRoot(), - SettingManagementConfigModule.forRoot(), + CoreModule, + ThemeSharedModule, ThemeLeptonXModule.forRoot(), SideMenuLayoutModule.forRoot(), - FeatureManagementModule.forRoot(), - InternetConnectionStatusComponent + InternetConnectionStatusComponent, ], declarations: [AppComponent], - providers: [APP_ROUTE_PROVIDER], + providers: [ + APP_ROUTE_PROVIDER, + provideAbpCore( + withOptions({ + environment, + registerLocaleFn: registerLocale(), + }) + ), + provideAbpOAuth(), + provideAbpThemeShared(), + provideSettingManagementConfig(), + provideAccountConfig(), + provideIdentityConfig(), + provideTenantManagementConfig(), + provideFeatureManagementConfig(), + ], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ar.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ar.json index 153f571276..b2b77a4d06 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ar.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ar.json @@ -1,6 +1,7 @@ { "culture": "ar", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "مرحبا", "Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", "Menu:Home": "الصفحة الرئيسية" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/cs.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/cs.json index 524a48e4eb..59e9214cb6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/cs.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vítejte", "Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", "Menu:Home": "Úvod" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/de.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/de.json index 05a00caf0c..ff9174ce3f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/de.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/de.json @@ -1,6 +1,7 @@ { "culture": "de", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Willkommen", "Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/en-GB.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/en-GB.json index cf24737f24..e12e470001 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/en-GB.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/en-GB.json @@ -1,6 +1,7 @@ { "culture": "en-GB", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome_Title", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/en.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/en.json index 3ee17295cb..bd393a3df6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/en.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/es.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/es.json index 6e138d3e1d..08ee992d31 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/es.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/es.json @@ -1,6 +1,7 @@ { "culture": "es", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bienvenido", "Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", "Menu:Home": "Inicio" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/fi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/fi.json index e963857efc..81854d597a 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/fi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/fi.json @@ -1,6 +1,7 @@ { "culture": "fi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Tervetuloa", "Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", "Menu:Home": "Koti" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/fr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/fr.json index b2c2b620f7..413e2fe3c9 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/fr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/fr.json @@ -1,7 +1,8 @@ { "culture": "fr", "texts": { - "Welcome_Title": "Bienvenue", + "AppName": "MyProjectName", + "Welcome_Title": "Bienvenue", "Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", "Menu:Home": "Accueil" } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hi.json index ec16d655b5..0c3b50f4d4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hi.json @@ -1,6 +1,7 @@ { "culture": "hi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "स्वागत हे", "Welcome_Text": "यह एबीपी फ्रेमवर्क के लिए एक न्यूनतम, सिंगल लेयर एप्लिकेशन स्टार्टअप टेम्प्लेट है।", "Menu:Home": "घर" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hr.json index 01dddf22ad..dd4f30ea68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hr.json @@ -1,8 +1,9 @@ { "culture": "hr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "Ovo je minimalistički jednoslojni predložak za pokretanje aplikacije za ABP Framework.", "Menu:Home": "Dom" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hu.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hu.json index 5833dcb415..9e56309d4d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hu.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/hu.json @@ -1,8 +1,9 @@ { "culture": "hu", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Üdvözlöm", "Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", "Menu:Home": "Kezdőlap" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/is.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/is.json index 080c0baca2..15dfcff12e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/is.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/is.json @@ -1,6 +1,7 @@ { "culture": "is", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Velkomin", "Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", "Menu:Home": "Heim" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/it.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/it.json index 57c3cf63d4..6d695dd3dc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/it.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/it.json @@ -1,6 +1,7 @@ { "culture": "it", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Benvenuto", "Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/nl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/nl.json index 525ee02d10..dcf8345459 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/nl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/nl.json @@ -1,6 +1,7 @@ { "culture": "nl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welkom", "Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/pl-PL.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/pl-PL.json index 2ebf70a5e1..9b582507fd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/pl-PL.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/pl-PL.json @@ -1,6 +1,7 @@ { "culture": "pl-PL", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Witaj", "Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/pt-BR.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/pt-BR.json index 96e45d3c3e..a60de1cd33 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/pt-BR.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/pt-BR.json @@ -1,6 +1,7 @@ { "culture": "pt-BR", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Seja bem-vindo!", "Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", "Menu:Home": "Principal" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ro-RO.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ro-RO.json index 48102a75b7..050b6feca6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ro-RO.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ro-RO.json @@ -1,6 +1,7 @@ { "culture": "ro-RO", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bun venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Acasă" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ru.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ru.json index eb4cc96b5d..263b7abc47 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ru.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bine ati venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Главная" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/sk.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/sk.json index 227d0a74cb..1df924082b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/sk.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/sk.json @@ -1,6 +1,7 @@ { "culture": "sk", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vitajte", "Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/sl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/sl.json index 29876afa0c..dcf0a7d293 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/sl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/tr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/tr.json index 6cbdac76a2..5b5700b716 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/tr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Hoşgeldiniz", "Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", "Menu:Home": "Ana sayfa" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/vi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/vi.json index f2e55bd7b0..d738315efd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/vi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Chào mừng bạn", "Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", "Menu:Home": "Trang chủ" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/zh-Hans.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/zh-Hans.json index a43366df48..0d42020ab0 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/zh-Hans.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/zh-Hans.json @@ -1,8 +1,9 @@ { "culture": "zh-Hans", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "欢迎", "Welcome_Text": "这是 ABP 框架的极简单层应用程序启动模板。", "Menu:Home": "首页" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/zh-Hant.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/zh-Hant.json index 7b4872c974..d00e97809c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/zh-Hant.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Localization/MyProjectName/zh-Hant.json @@ -1,6 +1,7 @@ { "culture": "zh-Hant", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "歡迎", "Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", "Menu:Home": "首頁" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameBrandingProvider.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameBrandingProvider.cs index fb6e75d065..26f6ca9fff 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameBrandingProvider.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameGlobalFeatureConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameGlobalFeatureConfigurator.cs new file mode 100644 index 0000000000..40b41df34d --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameGlobalFeatureConfigurator.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameGlobalFeatureConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + /* You can configure (enable/disable) global features of the used modules here. + * Please refer to the documentation to learn more about the Global Features System: + * https://docs.abp.io/en/abp/latest/Global-Features + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs index ce0a2c3f64..2aaac16fc8 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs @@ -154,6 +154,9 @@ public class MyProjectNameModule : AbpModule { options.IsBlazorWebApp = true; }); + + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModuleExtensionConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModuleExtensionConfigurator.cs new file mode 100644 index 0000000000..37d6056f67 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModuleExtensionConfigurator.cs @@ -0,0 +1,68 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameModuleExtensionConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + ConfigureExistingProperties(); + ConfigureExtraProperties(); + }); + } + + private static void ConfigureExistingProperties() + { + /* You can change max lengths for properties of the + * entities defined in the modules used by your application. + * + * Example: Change user and role name max lengths + + AbpUserConsts.MaxNameLength = 99; + IdentityRoleConsts.MaxNameLength = 99; + + * Notice: It is not suggested to change property lengths + * unless you really need it. Go with the standard values wherever possible. + * + * If you are using EF Core, you will need to run the add-migration command after your changes. + */ + } + + private static void ConfigureExtraProperties() + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to define these extra properties + * with a high level, easy to use API. + * + * Example: Add a new property to the user entity of the identity module + + ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty<string>( //property type: string + "SocialSecurityNumber", //property name + property => + { + //validation rules + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); + + //...other configurations for this property + } + ); + }); + }); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Module-Entity-Extensions + */ + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameDbContextFactory.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameDbContextFactory.cs index 91fdc9c6fb..b210a1269e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameDbContextFactory.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameDbContextFactory.cs @@ -11,6 +11,7 @@ public class MyProjectNameDbContextFactory : IDesignTimeDbContextFactory<MyProje // https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); //</TEMPLATE-REMOVE> + MyProjectNameEfCoreEntityExtensionMappings.Configure(); var configuration = BuildConfiguration(); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameEfCoreEntityExtensionMappings.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameEfCoreEntityExtensionMappings.cs new file mode 100644 index 0000000000..92a3230153 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameEfCoreEntityExtensionMappings.cs @@ -0,0 +1,41 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName.Data; + +public static class MyProjectNameEfCoreEntityExtensionMappings +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + + OneTimeRunner.Run(() => + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to map these extra properties to table fields in the database. + * + * USE THIS CLASS ONLY TO CONFIGURE EF CORE RELATED MAPPING. + * USE MyProjectNameModuleExtensionConfigurator CLASS (in the Domain.Shared project) + * FOR A HIGH LEVEL API TO DEFINE EXTRA PROPERTIES TO ENTITIES OF THE USED MODULES + * + * Example: Map a property to a table field: + + ObjectExtensionManager.Instance + .MapEfCoreProperty<IdentityUser, string>( + "MyProperty", + (entityBuilder, propertyBuilder) => + { + propertyBuilder.HasMaxLength(128); + } + ); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ar.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ar.json index 153f571276..b2b77a4d06 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ar.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ar.json @@ -1,6 +1,7 @@ { "culture": "ar", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "مرحبا", "Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", "Menu:Home": "الصفحة الرئيسية" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/cs.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/cs.json index 524a48e4eb..59e9214cb6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/cs.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vítejte", "Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", "Menu:Home": "Úvod" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/de.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/de.json index 05a00caf0c..ff9174ce3f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/de.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/de.json @@ -1,6 +1,7 @@ { "culture": "de", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Willkommen", "Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/en-GB.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/en-GB.json index cf24737f24..e12e470001 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/en-GB.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/en-GB.json @@ -1,6 +1,7 @@ { "culture": "en-GB", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome_Title", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/en.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/en.json index 3ee17295cb..bd393a3df6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/en.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/es.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/es.json index 6e138d3e1d..08ee992d31 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/es.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/es.json @@ -1,6 +1,7 @@ { "culture": "es", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bienvenido", "Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", "Menu:Home": "Inicio" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/fi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/fi.json index e963857efc..81854d597a 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/fi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/fi.json @@ -1,6 +1,7 @@ { "culture": "fi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Tervetuloa", "Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", "Menu:Home": "Koti" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/fr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/fr.json index b2c2b620f7..413e2fe3c9 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/fr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/fr.json @@ -1,7 +1,8 @@ { "culture": "fr", "texts": { - "Welcome_Title": "Bienvenue", + "AppName": "MyProjectName", + "Welcome_Title": "Bienvenue", "Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", "Menu:Home": "Accueil" } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hi.json index ec16d655b5..0c3b50f4d4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hi.json @@ -1,6 +1,7 @@ { "culture": "hi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "स्वागत हे", "Welcome_Text": "यह एबीपी फ्रेमवर्क के लिए एक न्यूनतम, सिंगल लेयर एप्लिकेशन स्टार्टअप टेम्प्लेट है।", "Menu:Home": "घर" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hr.json index 01dddf22ad..dd4f30ea68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hr.json @@ -1,8 +1,9 @@ { "culture": "hr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "Ovo je minimalistički jednoslojni predložak za pokretanje aplikacije za ABP Framework.", "Menu:Home": "Dom" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hu.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hu.json index 5833dcb415..9e56309d4d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hu.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/hu.json @@ -1,8 +1,9 @@ { "culture": "hu", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Üdvözlöm", "Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", "Menu:Home": "Kezdőlap" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/is.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/is.json index 080c0baca2..15dfcff12e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/is.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/is.json @@ -1,6 +1,7 @@ { "culture": "is", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Velkomin", "Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", "Menu:Home": "Heim" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/it.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/it.json index 57c3cf63d4..6d695dd3dc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/it.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/it.json @@ -1,6 +1,7 @@ { "culture": "it", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Benvenuto", "Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/nl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/nl.json index 525ee02d10..dcf8345459 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/nl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/nl.json @@ -1,6 +1,7 @@ { "culture": "nl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welkom", "Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/pl-PL.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/pl-PL.json index 2ebf70a5e1..9b582507fd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/pl-PL.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/pl-PL.json @@ -1,6 +1,7 @@ { "culture": "pl-PL", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Witaj", "Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/pt-BR.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/pt-BR.json index 96e45d3c3e..a60de1cd33 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/pt-BR.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/pt-BR.json @@ -1,6 +1,7 @@ { "culture": "pt-BR", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Seja bem-vindo!", "Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", "Menu:Home": "Principal" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ro-RO.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ro-RO.json index 48102a75b7..050b6feca6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ro-RO.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ro-RO.json @@ -1,6 +1,7 @@ { "culture": "ro-RO", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bun venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Acasă" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ru.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ru.json index eb4cc96b5d..263b7abc47 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ru.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bine ati venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Главная" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/sk.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/sk.json index 227d0a74cb..1df924082b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/sk.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/sk.json @@ -1,6 +1,7 @@ { "culture": "sk", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vitajte", "Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/sl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/sl.json index 29876afa0c..dcf0a7d293 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/sl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/tr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/tr.json index 6cbdac76a2..5b5700b716 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/tr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Hoşgeldiniz", "Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", "Menu:Home": "Ana sayfa" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/vi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/vi.json index f2e55bd7b0..d738315efd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/vi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Chào mừng bạn", "Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", "Menu:Home": "Trang chủ" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/zh-Hans.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/zh-Hans.json index a43366df48..0d42020ab0 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/zh-Hans.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/zh-Hans.json @@ -1,8 +1,9 @@ { "culture": "zh-Hans", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "欢迎", "Welcome_Text": "这是 ABP 框架的极简单层应用程序启动模板。", "Menu:Home": "首页" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/zh-Hant.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/zh-Hant.json index 7b4872c974..d00e97809c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/zh-Hant.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Localization/MyProjectName/zh-Hant.json @@ -1,6 +1,7 @@ { "culture": "zh-Hant", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "歡迎", "Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", "Menu:Home": "首頁" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBrandingProvider.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBrandingProvider.cs index fb6e75d065..26f6ca9fff 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBrandingProvider.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameGlobalFeatureConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameGlobalFeatureConfigurator.cs new file mode 100644 index 0000000000..40b41df34d --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameGlobalFeatureConfigurator.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameGlobalFeatureConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + /* You can configure (enable/disable) global features of the used modules here. + * Please refer to the documentation to learn more about the Global Features System: + * https://docs.abp.io/en/abp/latest/Global-Features + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs index ea0785fe24..f869eec2e4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs @@ -156,6 +156,10 @@ public class MyProjectNameModule : AbpModule { options.IsBlazorWebApp = true; }); + + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + MyProjectNameEfCoreEntityExtensionMappings.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModuleExtensionConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModuleExtensionConfigurator.cs new file mode 100644 index 0000000000..37d6056f67 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModuleExtensionConfigurator.cs @@ -0,0 +1,68 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameModuleExtensionConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + ConfigureExistingProperties(); + ConfigureExtraProperties(); + }); + } + + private static void ConfigureExistingProperties() + { + /* You can change max lengths for properties of the + * entities defined in the modules used by your application. + * + * Example: Change user and role name max lengths + + AbpUserConsts.MaxNameLength = 99; + IdentityRoleConsts.MaxNameLength = 99; + + * Notice: It is not suggested to change property lengths + * unless you really need it. Go with the standard values wherever possible. + * + * If you are using EF Core, you will need to run the add-migration command after your changes. + */ + } + + private static void ConfigureExtraProperties() + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to define these extra properties + * with a high level, easy to use API. + * + * Example: Add a new property to the user entity of the identity module + + ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty<string>( //property type: string + "SocialSecurityNumber", //property name + property => + { + //validation rules + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); + + //...other configurations for this property + } + ); + }); + }); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Module-Entity-Extensions + */ + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBrandingProvider.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBrandingProvider.cs index fb6e75d065..26f6ca9fff 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBrandingProvider.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Data/MyProjectNameDbContextFactory.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Data/MyProjectNameDbContextFactory.cs index 91fdc9c6fb..26b75eba68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Data/MyProjectNameDbContextFactory.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Data/MyProjectNameDbContextFactory.cs @@ -11,7 +11,8 @@ public class MyProjectNameDbContextFactory : IDesignTimeDbContextFactory<MyProje // https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); //</TEMPLATE-REMOVE> - + MyProjectNameEfCoreEntityExtensionMappings.Configure(); + var configuration = BuildConfiguration(); var builder = new DbContextOptionsBuilder<MyProjectNameDbContext>() diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Data/MyProjectNameEfCoreEntityExtensionMappings.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Data/MyProjectNameEfCoreEntityExtensionMappings.cs new file mode 100644 index 0000000000..92a3230153 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Data/MyProjectNameEfCoreEntityExtensionMappings.cs @@ -0,0 +1,41 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName.Data; + +public static class MyProjectNameEfCoreEntityExtensionMappings +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + + OneTimeRunner.Run(() => + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to map these extra properties to table fields in the database. + * + * USE THIS CLASS ONLY TO CONFIGURE EF CORE RELATED MAPPING. + * USE MyProjectNameModuleExtensionConfigurator CLASS (in the Domain.Shared project) + * FOR A HIGH LEVEL API TO DEFINE EXTRA PROPERTIES TO ENTITIES OF THE USED MODULES + * + * Example: Map a property to a table field: + + ObjectExtensionManager.Instance + .MapEfCoreProperty<IdentityUser, string>( + "MyProperty", + (entityBuilder, propertyBuilder) => + { + propertyBuilder.HasMaxLength(128); + } + ); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyProjectNameHostModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyProjectNameHostModule.cs index b8522fb170..7767fec5cb 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyProjectNameHostModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyProjectNameHostModule.cs @@ -138,6 +138,8 @@ public class MyProjectNameHostModule : AbpModule serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "00000000-0000-0000-0000-000000000000"); }); } + + MyProjectNameEfCoreEntityExtensionMappings.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ar.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ar.json index 153f571276..b2b77a4d06 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ar.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ar.json @@ -1,6 +1,7 @@ { "culture": "ar", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "مرحبا", "Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", "Menu:Home": "الصفحة الرئيسية" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/cs.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/cs.json index 524a48e4eb..59e9214cb6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/cs.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vítejte", "Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", "Menu:Home": "Úvod" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/de.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/de.json index 05a00caf0c..ff9174ce3f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/de.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/de.json @@ -1,6 +1,7 @@ { "culture": "de", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Willkommen", "Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/en-GB.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/en-GB.json index cf24737f24..e12e470001 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/en-GB.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/en-GB.json @@ -1,6 +1,7 @@ { "culture": "en-GB", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome_Title", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/en.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/en.json index 3ee17295cb..bd393a3df6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/en.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/es.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/es.json index 6e138d3e1d..08ee992d31 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/es.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/es.json @@ -1,6 +1,7 @@ { "culture": "es", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bienvenido", "Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", "Menu:Home": "Inicio" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/fi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/fi.json index e963857efc..81854d597a 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/fi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/fi.json @@ -1,6 +1,7 @@ { "culture": "fi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Tervetuloa", "Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", "Menu:Home": "Koti" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/fr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/fr.json index b2c2b620f7..413e2fe3c9 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/fr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/fr.json @@ -1,7 +1,8 @@ { "culture": "fr", "texts": { - "Welcome_Title": "Bienvenue", + "AppName": "MyProjectName", + "Welcome_Title": "Bienvenue", "Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", "Menu:Home": "Accueil" } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hi.json index ec16d655b5..0c3b50f4d4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hi.json @@ -1,6 +1,7 @@ { "culture": "hi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "स्वागत हे", "Welcome_Text": "यह एबीपी फ्रेमवर्क के लिए एक न्यूनतम, सिंगल लेयर एप्लिकेशन स्टार्टअप टेम्प्लेट है।", "Menu:Home": "घर" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hr.json index 01dddf22ad..dd4f30ea68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hr.json @@ -1,8 +1,9 @@ { "culture": "hr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "Ovo je minimalistički jednoslojni predložak za pokretanje aplikacije za ABP Framework.", "Menu:Home": "Dom" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hu.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hu.json index 5833dcb415..9e56309d4d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hu.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/hu.json @@ -1,8 +1,9 @@ { "culture": "hu", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Üdvözlöm", "Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", "Menu:Home": "Kezdőlap" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/is.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/is.json index 080c0baca2..15dfcff12e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/is.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/is.json @@ -1,6 +1,7 @@ { "culture": "is", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Velkomin", "Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", "Menu:Home": "Heim" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/it.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/it.json index 57c3cf63d4..6d695dd3dc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/it.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/it.json @@ -1,6 +1,7 @@ { "culture": "it", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Benvenuto", "Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/nl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/nl.json index 525ee02d10..dcf8345459 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/nl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/nl.json @@ -1,6 +1,7 @@ { "culture": "nl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welkom", "Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/pl-PL.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/pl-PL.json index 2ebf70a5e1..9b582507fd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/pl-PL.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/pl-PL.json @@ -1,6 +1,7 @@ { "culture": "pl-PL", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Witaj", "Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/pt-BR.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/pt-BR.json index 96e45d3c3e..a60de1cd33 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/pt-BR.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/pt-BR.json @@ -1,6 +1,7 @@ { "culture": "pt-BR", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Seja bem-vindo!", "Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", "Menu:Home": "Principal" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ro-RO.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ro-RO.json index 48102a75b7..050b6feca6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ro-RO.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ro-RO.json @@ -1,6 +1,7 @@ { "culture": "ro-RO", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bun venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Acasă" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ru.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ru.json index eb4cc96b5d..263b7abc47 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ru.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bine ati venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Главная" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/sk.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/sk.json index 227d0a74cb..1df924082b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/sk.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/sk.json @@ -1,6 +1,7 @@ { "culture": "sk", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vitajte", "Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/sl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/sl.json index 29876afa0c..dcf0a7d293 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/sl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/tr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/tr.json index 6cbdac76a2..5b5700b716 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/tr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Hoşgeldiniz", "Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", "Menu:Home": "Ana sayfa" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/vi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/vi.json index f2e55bd7b0..d738315efd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/vi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Chào mừng bạn", "Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", "Menu:Home": "Trang chủ" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/zh-Hans.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/zh-Hans.json index a43366df48..0d42020ab0 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/zh-Hans.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/zh-Hans.json @@ -1,8 +1,9 @@ { "culture": "zh-Hans", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "欢迎", "Welcome_Text": "这是 ABP 框架的极简单层应用程序启动模板。", "Menu:Home": "首页" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/zh-Hant.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/zh-Hant.json index 7b4872c974..d00e97809c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/zh-Hant.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/Localization/MyProjectName/zh-Hant.json @@ -1,6 +1,7 @@ { "culture": "zh-Hant", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "歡迎", "Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", "Menu:Home": "首頁" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameContractsModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameContractsModule.cs index e1661f41ec..23dff541d1 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameContractsModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameContractsModule.cs @@ -25,6 +25,12 @@ namespace MyCompanyName.MyProjectName; )] public class MyProjectNameContractsModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + } + public override void ConfigureServices(ServiceConfigurationContext context) { Configure<AbpVirtualFileSystemOptions>(options => diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameGlobalFeatureConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameGlobalFeatureConfigurator.cs new file mode 100644 index 0000000000..40b41df34d --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameGlobalFeatureConfigurator.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameGlobalFeatureConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + /* You can configure (enable/disable) global features of the used modules here. + * Please refer to the documentation to learn more about the Global Features System: + * https://docs.abp.io/en/abp/latest/Global-Features + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameModuleExtensionConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameModuleExtensionConfigurator.cs new file mode 100644 index 0000000000..37d6056f67 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyProjectNameModuleExtensionConfigurator.cs @@ -0,0 +1,68 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameModuleExtensionConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + ConfigureExistingProperties(); + ConfigureExtraProperties(); + }); + } + + private static void ConfigureExistingProperties() + { + /* You can change max lengths for properties of the + * entities defined in the modules used by your application. + * + * Example: Change user and role name max lengths + + AbpUserConsts.MaxNameLength = 99; + IdentityRoleConsts.MaxNameLength = 99; + + * Notice: It is not suggested to change property lengths + * unless you really need it. Go with the standard values wherever possible. + * + * If you are using EF Core, you will need to run the add-migration command after your changes. + */ + } + + private static void ConfigureExtraProperties() + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to define these extra properties + * with a high level, easy to use API. + * + * Example: Add a new property to the user entity of the identity module + + ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty<string>( //property type: string + "SocialSecurityNumber", //property name + property => + { + //validation rules + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); + + //...other configurations for this property + } + ); + }); + }); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Module-Entity-Extensions + */ + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ar.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ar.json index 153f571276..b2b77a4d06 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ar.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ar.json @@ -1,6 +1,7 @@ { "culture": "ar", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "مرحبا", "Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", "Menu:Home": "الصفحة الرئيسية" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/cs.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/cs.json index 524a48e4eb..59e9214cb6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/cs.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vítejte", "Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", "Menu:Home": "Úvod" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/de.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/de.json index 05a00caf0c..ff9174ce3f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/de.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/de.json @@ -1,6 +1,7 @@ { "culture": "de", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Willkommen", "Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/en-GB.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/en-GB.json index cf24737f24..e12e470001 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/en-GB.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/en-GB.json @@ -1,6 +1,7 @@ { "culture": "en-GB", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome_Title", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/en.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/en.json index 3ee17295cb..bd393a3df6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/en.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/es.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/es.json index 6e138d3e1d..08ee992d31 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/es.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/es.json @@ -1,6 +1,7 @@ { "culture": "es", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bienvenido", "Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", "Menu:Home": "Inicio" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/fi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/fi.json index e963857efc..81854d597a 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/fi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/fi.json @@ -1,6 +1,7 @@ { "culture": "fi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Tervetuloa", "Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", "Menu:Home": "Koti" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/fr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/fr.json index b2c2b620f7..413e2fe3c9 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/fr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/fr.json @@ -1,7 +1,8 @@ { "culture": "fr", "texts": { - "Welcome_Title": "Bienvenue", + "AppName": "MyProjectName", + "Welcome_Title": "Bienvenue", "Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", "Menu:Home": "Accueil" } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hi.json index ec16d655b5..0c3b50f4d4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hi.json @@ -1,6 +1,7 @@ { "culture": "hi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "स्वागत हे", "Welcome_Text": "यह एबीपी फ्रेमवर्क के लिए एक न्यूनतम, सिंगल लेयर एप्लिकेशन स्टार्टअप टेम्प्लेट है।", "Menu:Home": "घर" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hr.json index 01dddf22ad..dd4f30ea68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hr.json @@ -1,8 +1,9 @@ { "culture": "hr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "Ovo je minimalistički jednoslojni predložak za pokretanje aplikacije za ABP Framework.", "Menu:Home": "Dom" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hu.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hu.json index 5833dcb415..9e56309d4d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hu.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/hu.json @@ -1,8 +1,9 @@ { "culture": "hu", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Üdvözlöm", "Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", "Menu:Home": "Kezdőlap" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/is.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/is.json index 080c0baca2..15dfcff12e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/is.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/is.json @@ -1,6 +1,7 @@ { "culture": "is", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Velkomin", "Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", "Menu:Home": "Heim" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/it.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/it.json index 57c3cf63d4..6d695dd3dc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/it.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/it.json @@ -1,6 +1,7 @@ { "culture": "it", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Benvenuto", "Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/nl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/nl.json index 525ee02d10..dcf8345459 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/nl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/nl.json @@ -1,6 +1,7 @@ { "culture": "nl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welkom", "Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/pl-PL.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/pl-PL.json index 2ebf70a5e1..9b582507fd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/pl-PL.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/pl-PL.json @@ -1,6 +1,7 @@ { "culture": "pl-PL", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Witaj", "Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/pt-BR.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/pt-BR.json index 96e45d3c3e..a60de1cd33 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/pt-BR.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/pt-BR.json @@ -1,6 +1,7 @@ { "culture": "pt-BR", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Seja bem-vindo!", "Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", "Menu:Home": "Principal" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ro-RO.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ro-RO.json index 48102a75b7..050b6feca6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ro-RO.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ro-RO.json @@ -1,6 +1,7 @@ { "culture": "ro-RO", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bun venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Acasă" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ru.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ru.json index eb4cc96b5d..263b7abc47 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ru.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bine ati venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Главная" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/sk.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/sk.json index 227d0a74cb..1df924082b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/sk.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/sk.json @@ -1,6 +1,7 @@ { "culture": "sk", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vitajte", "Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/sl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/sl.json index 29876afa0c..dcf0a7d293 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/sl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/tr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/tr.json index 6cbdac76a2..5b5700b716 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/tr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Hoşgeldiniz", "Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", "Menu:Home": "Ana sayfa" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/vi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/vi.json index f2e55bd7b0..d738315efd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/vi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Chào mừng bạn", "Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", "Menu:Home": "Trang chủ" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/zh-Hans.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/zh-Hans.json index a43366df48..0d42020ab0 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/zh-Hans.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/zh-Hans.json @@ -1,8 +1,9 @@ { "culture": "zh-Hans", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "欢迎", "Welcome_Text": "这是 ABP 框架的极简单层应用程序启动模板。", "Menu:Home": "首页" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/zh-Hant.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/zh-Hant.json index 7b4872c974..d00e97809c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/zh-Hant.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Localization/MyProjectName/zh-Hant.json @@ -1,6 +1,7 @@ { "culture": "zh-Hant", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "歡迎", "Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", "Menu:Home": "首頁" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameBrandingProvider.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameBrandingProvider.cs new file mode 100644 index 0000000000..26f6ca9fff --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameBrandingProvider.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Ui.Branding; + +namespace MyCompanyName.MyProjectName; + +[Dependency(ReplaceServices = true)] +public class MyProjectNameBrandingProvider : DefaultBrandingProvider +{ + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameGlobalFeatureConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameGlobalFeatureConfigurator.cs new file mode 100644 index 0000000000..40b41df34d --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameGlobalFeatureConfigurator.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameGlobalFeatureConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + /* You can configure (enable/disable) global features of the used modules here. + * Please refer to the documentation to learn more about the Global Features System: + * https://docs.abp.io/en/abp/latest/Global-Features + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs index 098a9970ad..388b34fea6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs @@ -134,6 +134,9 @@ public class MyProjectNameModule : AbpModule serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "00000000-0000-0000-0000-000000000000"); }); } + + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModuleExtensionConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModuleExtensionConfigurator.cs new file mode 100644 index 0000000000..37d6056f67 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModuleExtensionConfigurator.cs @@ -0,0 +1,68 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameModuleExtensionConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + ConfigureExistingProperties(); + ConfigureExtraProperties(); + }); + } + + private static void ConfigureExistingProperties() + { + /* You can change max lengths for properties of the + * entities defined in the modules used by your application. + * + * Example: Change user and role name max lengths + + AbpUserConsts.MaxNameLength = 99; + IdentityRoleConsts.MaxNameLength = 99; + + * Notice: It is not suggested to change property lengths + * unless you really need it. Go with the standard values wherever possible. + * + * If you are using EF Core, you will need to run the add-migration command after your changes. + */ + } + + private static void ConfigureExtraProperties() + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to define these extra properties + * with a high level, easy to use API. + * + * Example: Add a new property to the user entity of the identity module + + ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty<string>( //property type: string + "SocialSecurityNumber", //property name + property => + { + //validation rules + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); + + //...other configurations for this property + } + ); + }); + }); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Module-Entity-Extensions + */ + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameDbContextFactory.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameDbContextFactory.cs index 91fdc9c6fb..b210a1269e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameDbContextFactory.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameDbContextFactory.cs @@ -11,6 +11,7 @@ public class MyProjectNameDbContextFactory : IDesignTimeDbContextFactory<MyProje // https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); //</TEMPLATE-REMOVE> + MyProjectNameEfCoreEntityExtensionMappings.Configure(); var configuration = BuildConfiguration(); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameEfCoreEntityExtensionMappings.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameEfCoreEntityExtensionMappings.cs new file mode 100644 index 0000000000..92a3230153 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameEfCoreEntityExtensionMappings.cs @@ -0,0 +1,41 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName.Data; + +public static class MyProjectNameEfCoreEntityExtensionMappings +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + + OneTimeRunner.Run(() => + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to map these extra properties to table fields in the database. + * + * USE THIS CLASS ONLY TO CONFIGURE EF CORE RELATED MAPPING. + * USE MyProjectNameModuleExtensionConfigurator CLASS (in the Domain.Shared project) + * FOR A HIGH LEVEL API TO DEFINE EXTRA PROPERTIES TO ENTITIES OF THE USED MODULES + * + * Example: Map a property to a table field: + + ObjectExtensionManager.Instance + .MapEfCoreProperty<IdentityUser, string>( + "MyProperty", + (entityBuilder, propertyBuilder) => + { + propertyBuilder.HasMaxLength(128); + } + ); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ar.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ar.json index 153f571276..b2b77a4d06 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ar.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ar.json @@ -1,6 +1,7 @@ { "culture": "ar", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "مرحبا", "Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", "Menu:Home": "الصفحة الرئيسية" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/cs.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/cs.json index 524a48e4eb..59e9214cb6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/cs.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vítejte", "Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", "Menu:Home": "Úvod" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/de.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/de.json index 05a00caf0c..ff9174ce3f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/de.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/de.json @@ -1,6 +1,7 @@ { "culture": "de", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Willkommen", "Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/en-GB.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/en-GB.json index cf24737f24..e12e470001 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/en-GB.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/en-GB.json @@ -1,6 +1,7 @@ { "culture": "en-GB", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome_Title", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/en.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/en.json index 3ee17295cb..bd393a3df6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/en.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/es.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/es.json index 6e138d3e1d..08ee992d31 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/es.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/es.json @@ -1,6 +1,7 @@ { "culture": "es", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bienvenido", "Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", "Menu:Home": "Inicio" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/fi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/fi.json index e963857efc..81854d597a 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/fi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/fi.json @@ -1,6 +1,7 @@ { "culture": "fi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Tervetuloa", "Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", "Menu:Home": "Koti" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/fr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/fr.json index b2c2b620f7..413e2fe3c9 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/fr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/fr.json @@ -1,7 +1,8 @@ { "culture": "fr", "texts": { - "Welcome_Title": "Bienvenue", + "AppName": "MyProjectName", + "Welcome_Title": "Bienvenue", "Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", "Menu:Home": "Accueil" } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hi.json index ec16d655b5..0c3b50f4d4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hi.json @@ -1,6 +1,7 @@ { "culture": "hi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "स्वागत हे", "Welcome_Text": "यह एबीपी फ्रेमवर्क के लिए एक न्यूनतम, सिंगल लेयर एप्लिकेशन स्टार्टअप टेम्प्लेट है।", "Menu:Home": "घर" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hr.json index 01dddf22ad..dd4f30ea68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hr.json @@ -1,8 +1,9 @@ { "culture": "hr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "Ovo je minimalistički jednoslojni predložak za pokretanje aplikacije za ABP Framework.", "Menu:Home": "Dom" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hu.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hu.json index 5833dcb415..9e56309d4d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hu.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/hu.json @@ -1,8 +1,9 @@ { "culture": "hu", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Üdvözlöm", "Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", "Menu:Home": "Kezdőlap" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/is.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/is.json index 080c0baca2..15dfcff12e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/is.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/is.json @@ -1,6 +1,7 @@ { "culture": "is", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Velkomin", "Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", "Menu:Home": "Heim" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/it.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/it.json index 57c3cf63d4..6d695dd3dc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/it.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/it.json @@ -1,6 +1,7 @@ { "culture": "it", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Benvenuto", "Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/nl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/nl.json index 525ee02d10..dcf8345459 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/nl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/nl.json @@ -1,6 +1,7 @@ { "culture": "nl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welkom", "Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/pl-PL.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/pl-PL.json index 2ebf70a5e1..9b582507fd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/pl-PL.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/pl-PL.json @@ -1,6 +1,7 @@ { "culture": "pl-PL", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Witaj", "Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/pt-BR.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/pt-BR.json index 96e45d3c3e..a60de1cd33 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/pt-BR.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/pt-BR.json @@ -1,6 +1,7 @@ { "culture": "pt-BR", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Seja bem-vindo!", "Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", "Menu:Home": "Principal" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ro-RO.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ro-RO.json index 48102a75b7..050b6feca6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ro-RO.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ro-RO.json @@ -1,6 +1,7 @@ { "culture": "ro-RO", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bun venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Acasă" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ru.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ru.json index eb4cc96b5d..263b7abc47 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ru.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bine ati venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Главная" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/sk.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/sk.json index 227d0a74cb..1df924082b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/sk.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/sk.json @@ -1,6 +1,7 @@ { "culture": "sk", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vitajte", "Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/sl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/sl.json index 29876afa0c..dcf0a7d293 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/sl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/tr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/tr.json index 6cbdac76a2..5b5700b716 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/tr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Hoşgeldiniz", "Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", "Menu:Home": "Ana sayfa" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/vi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/vi.json index f2e55bd7b0..d738315efd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/vi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Chào mừng bạn", "Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", "Menu:Home": "Trang chủ" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/zh-Hans.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/zh-Hans.json index a43366df48..0d42020ab0 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/zh-Hans.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/zh-Hans.json @@ -1,8 +1,9 @@ { "culture": "zh-Hans", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "欢迎", "Welcome_Text": "这是 ABP 框架的极简单层应用程序启动模板。", "Menu:Home": "首页" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/zh-Hant.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/zh-Hant.json index 7b4872c974..d00e97809c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/zh-Hant.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Localization/MyProjectName/zh-Hant.json @@ -1,6 +1,7 @@ { "culture": "zh-Hant", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "歡迎", "Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", "Menu:Home": "首頁" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameBrandingProvider.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameBrandingProvider.cs new file mode 100644 index 0000000000..26f6ca9fff --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameBrandingProvider.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Ui.Branding; + +namespace MyCompanyName.MyProjectName; + +[Dependency(ReplaceServices = true)] +public class MyProjectNameBrandingProvider : DefaultBrandingProvider +{ + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameGlobalFeatureConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameGlobalFeatureConfigurator.cs new file mode 100644 index 0000000000..40b41df34d --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameGlobalFeatureConfigurator.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameGlobalFeatureConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + /* You can configure (enable/disable) global features of the used modules here. + * Please refer to the documentation to learn more about the Global Features System: + * https://docs.abp.io/en/abp/latest/Global-Features + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs index 54f224062f..36cfc45062 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs @@ -137,6 +137,10 @@ public class MyProjectNameModule : AbpModule serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "00000000-0000-0000-0000-000000000000"); }); } + + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + MyProjectNameEfCoreEntityExtensionMappings.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModuleExtensionConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModuleExtensionConfigurator.cs new file mode 100644 index 0000000000..37d6056f67 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModuleExtensionConfigurator.cs @@ -0,0 +1,68 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameModuleExtensionConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + ConfigureExistingProperties(); + ConfigureExtraProperties(); + }); + } + + private static void ConfigureExistingProperties() + { + /* You can change max lengths for properties of the + * entities defined in the modules used by your application. + * + * Example: Change user and role name max lengths + + AbpUserConsts.MaxNameLength = 99; + IdentityRoleConsts.MaxNameLength = 99; + + * Notice: It is not suggested to change property lengths + * unless you really need it. Go with the standard values wherever possible. + * + * If you are using EF Core, you will need to run the add-migration command after your changes. + */ + } + + private static void ConfigureExtraProperties() + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to define these extra properties + * with a high level, easy to use API. + * + * Example: Add a new property to the user entity of the identity module + + ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty<string>( //property type: string + "SocialSecurityNumber", //property name + property => + { + //validation rules + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); + + //...other configurations for this property + } + ); + }); + }); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Module-Entity-Extensions + */ + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ar.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ar.json index 153f571276..b2b77a4d06 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ar.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ar.json @@ -1,6 +1,7 @@ { "culture": "ar", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "مرحبا", "Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", "Menu:Home": "الصفحة الرئيسية" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/cs.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/cs.json index 524a48e4eb..59e9214cb6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/cs.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vítejte", "Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", "Menu:Home": "Úvod" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/de.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/de.json index 05a00caf0c..ff9174ce3f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/de.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/de.json @@ -1,6 +1,7 @@ { "culture": "de", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Willkommen", "Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/en-GB.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/en-GB.json index cf24737f24..e12e470001 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/en-GB.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/en-GB.json @@ -1,6 +1,7 @@ { "culture": "en-GB", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome_Title", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/en.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/en.json index 3ee17295cb..bd393a3df6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/en.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/es.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/es.json index 6e138d3e1d..08ee992d31 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/es.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/es.json @@ -1,6 +1,7 @@ { "culture": "es", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bienvenido", "Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", "Menu:Home": "Inicio" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/fi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/fi.json index e963857efc..81854d597a 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/fi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/fi.json @@ -1,6 +1,7 @@ { "culture": "fi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Tervetuloa", "Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", "Menu:Home": "Koti" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/fr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/fr.json index b2c2b620f7..413e2fe3c9 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/fr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/fr.json @@ -1,7 +1,8 @@ { "culture": "fr", "texts": { - "Welcome_Title": "Bienvenue", + "AppName": "MyProjectName", + "Welcome_Title": "Bienvenue", "Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", "Menu:Home": "Accueil" } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hi.json index ec16d655b5..0c3b50f4d4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hi.json @@ -1,6 +1,7 @@ { "culture": "hi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "स्वागत हे", "Welcome_Text": "यह एबीपी फ्रेमवर्क के लिए एक न्यूनतम, सिंगल लेयर एप्लिकेशन स्टार्टअप टेम्प्लेट है।", "Menu:Home": "घर" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hr.json index 01dddf22ad..dd4f30ea68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hr.json @@ -1,8 +1,9 @@ { "culture": "hr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "Ovo je minimalistički jednoslojni predložak za pokretanje aplikacije za ABP Framework.", "Menu:Home": "Dom" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hu.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hu.json index 5833dcb415..9e56309d4d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hu.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/hu.json @@ -1,8 +1,9 @@ { "culture": "hu", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Üdvözlöm", "Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", "Menu:Home": "Kezdőlap" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/is.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/is.json index 080c0baca2..15dfcff12e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/is.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/is.json @@ -1,6 +1,7 @@ { "culture": "is", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Velkomin", "Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", "Menu:Home": "Heim" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/it.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/it.json index 57c3cf63d4..6d695dd3dc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/it.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/it.json @@ -1,6 +1,7 @@ { "culture": "it", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Benvenuto", "Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/nl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/nl.json index 525ee02d10..dcf8345459 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/nl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/nl.json @@ -1,6 +1,7 @@ { "culture": "nl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welkom", "Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/pl-PL.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/pl-PL.json index 2ebf70a5e1..9b582507fd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/pl-PL.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/pl-PL.json @@ -1,6 +1,7 @@ { "culture": "pl-PL", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Witaj", "Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/pt-BR.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/pt-BR.json index 96e45d3c3e..a60de1cd33 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/pt-BR.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/pt-BR.json @@ -1,6 +1,7 @@ { "culture": "pt-BR", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Seja bem-vindo!", "Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", "Menu:Home": "Principal" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ro-RO.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ro-RO.json index 48102a75b7..050b6feca6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ro-RO.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ro-RO.json @@ -1,6 +1,7 @@ { "culture": "ro-RO", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bun venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Acasă" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ru.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ru.json index eb4cc96b5d..263b7abc47 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ru.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bine ati venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Главная" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/sk.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/sk.json index 227d0a74cb..1df924082b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/sk.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/sk.json @@ -1,6 +1,7 @@ { "culture": "sk", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vitajte", "Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/sl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/sl.json index 29876afa0c..dcf0a7d293 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/sl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/tr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/tr.json index 6cbdac76a2..5b5700b716 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/tr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Hoşgeldiniz", "Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", "Menu:Home": "Ana sayfa" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/vi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/vi.json index f2e55bd7b0..d738315efd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/vi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Chào mừng bạn", "Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", "Menu:Home": "Trang chủ" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/zh-Hans.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/zh-Hans.json index a43366df48..0d42020ab0 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/zh-Hans.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/zh-Hans.json @@ -1,8 +1,9 @@ { "culture": "zh-Hans", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "欢迎", "Welcome_Text": "这是 ABP 框架的极简单层应用程序启动模板。", "Menu:Home": "首页" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/zh-Hant.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/zh-Hant.json index 7b4872c974..d00e97809c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/zh-Hant.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Localization/MyProjectName/zh-Hant.json @@ -1,6 +1,7 @@ { "culture": "zh-Hant", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "歡迎", "Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", "Menu:Home": "首頁" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameBrandingProvider.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameBrandingProvider.cs index fb6e75d065..26f6ca9fff 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameBrandingProvider.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameGlobalFeatureConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameGlobalFeatureConfigurator.cs new file mode 100644 index 0000000000..40b41df34d --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameGlobalFeatureConfigurator.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameGlobalFeatureConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + /* You can configure (enable/disable) global features of the used modules here. + * Please refer to the documentation to learn more about the Global Features System: + * https://docs.abp.io/en/abp/latest/Global-Features + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModule.cs index 43df51935e..5484aff3b5 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModule.cs @@ -140,6 +140,9 @@ public class MyProjectNameModule : AbpModule serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "00000000-0000-0000-0000-000000000000"); }); } + + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModuleExtensionConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModuleExtensionConfigurator.cs new file mode 100644 index 0000000000..37d6056f67 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModuleExtensionConfigurator.cs @@ -0,0 +1,68 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameModuleExtensionConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + ConfigureExistingProperties(); + ConfigureExtraProperties(); + }); + } + + private static void ConfigureExistingProperties() + { + /* You can change max lengths for properties of the + * entities defined in the modules used by your application. + * + * Example: Change user and role name max lengths + + AbpUserConsts.MaxNameLength = 99; + IdentityRoleConsts.MaxNameLength = 99; + + * Notice: It is not suggested to change property lengths + * unless you really need it. Go with the standard values wherever possible. + * + * If you are using EF Core, you will need to run the add-migration command after your changes. + */ + } + + private static void ConfigureExtraProperties() + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to define these extra properties + * with a high level, easy to use API. + * + * Example: Add a new property to the user entity of the identity module + + ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty<string>( //property type: string + "SocialSecurityNumber", //property name + property => + { + //validation rules + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); + + //...other configurations for this property + } + ); + }); + }); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Module-Entity-Extensions + */ + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameDbContextFactory.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameDbContextFactory.cs index 91fdc9c6fb..26b75eba68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameDbContextFactory.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameDbContextFactory.cs @@ -11,7 +11,8 @@ public class MyProjectNameDbContextFactory : IDesignTimeDbContextFactory<MyProje // https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); //</TEMPLATE-REMOVE> - + MyProjectNameEfCoreEntityExtensionMappings.Configure(); + var configuration = BuildConfiguration(); var builder = new DbContextOptionsBuilder<MyProjectNameDbContext>() diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameEfCoreEntityExtensionMappings.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameEfCoreEntityExtensionMappings.cs new file mode 100644 index 0000000000..92a3230153 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameEfCoreEntityExtensionMappings.cs @@ -0,0 +1,41 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName.Data; + +public static class MyProjectNameEfCoreEntityExtensionMappings +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + + OneTimeRunner.Run(() => + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to map these extra properties to table fields in the database. + * + * USE THIS CLASS ONLY TO CONFIGURE EF CORE RELATED MAPPING. + * USE MyProjectNameModuleExtensionConfigurator CLASS (in the Domain.Shared project) + * FOR A HIGH LEVEL API TO DEFINE EXTRA PROPERTIES TO ENTITIES OF THE USED MODULES + * + * Example: Map a property to a table field: + + ObjectExtensionManager.Instance + .MapEfCoreProperty<IdentityUser, string>( + "MyProperty", + (entityBuilder, propertyBuilder) => + { + propertyBuilder.HasMaxLength(128); + } + ); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ar.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ar.json index 153f571276..b2b77a4d06 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ar.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ar.json @@ -1,6 +1,7 @@ { "culture": "ar", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "مرحبا", "Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", "Menu:Home": "الصفحة الرئيسية" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/cs.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/cs.json index 524a48e4eb..59e9214cb6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/cs.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vítejte", "Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", "Menu:Home": "Úvod" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/de.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/de.json index 05a00caf0c..ff9174ce3f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/de.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/de.json @@ -1,6 +1,7 @@ { "culture": "de", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Willkommen", "Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/en-GB.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/en-GB.json index cf24737f24..e12e470001 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/en-GB.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/en-GB.json @@ -1,6 +1,7 @@ { "culture": "en-GB", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welcome_Title", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/en.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/en.json index 3ee17295cb..aa462bca8d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/en.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/en.json @@ -1,7 +1,7 @@ { "culture": "en", "texts": { - "Welcome_Title": "Welcome", + "AppName": "MyProjectName", "Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", "Menu:Home": "Home" } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/es.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/es.json index 6e138d3e1d..08ee992d31 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/es.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/es.json @@ -1,6 +1,7 @@ { "culture": "es", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bienvenido", "Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", "Menu:Home": "Inicio" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/fi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/fi.json index e963857efc..81854d597a 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/fi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/fi.json @@ -1,6 +1,7 @@ { "culture": "fi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Tervetuloa", "Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", "Menu:Home": "Koti" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/fr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/fr.json index b2c2b620f7..413e2fe3c9 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/fr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/fr.json @@ -1,7 +1,8 @@ { "culture": "fr", "texts": { - "Welcome_Title": "Bienvenue", + "AppName": "MyProjectName", + "Welcome_Title": "Bienvenue", "Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", "Menu:Home": "Accueil" } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hi.json index ec16d655b5..0c3b50f4d4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hi.json @@ -1,6 +1,7 @@ { "culture": "hi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "स्वागत हे", "Welcome_Text": "यह एबीपी फ्रेमवर्क के लिए एक न्यूनतम, सिंगल लेयर एप्लिकेशन स्टार्टअप टेम्प्लेट है।", "Menu:Home": "घर" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hr.json index 01dddf22ad..dd4f30ea68 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hr.json @@ -1,8 +1,9 @@ { "culture": "hr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "Ovo je minimalistički jednoslojni predložak za pokretanje aplikacije za ABP Framework.", "Menu:Home": "Dom" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hu.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hu.json index 5833dcb415..9e56309d4d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hu.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/hu.json @@ -1,8 +1,9 @@ { "culture": "hu", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Üdvözlöm", "Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", "Menu:Home": "Kezdőlap" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/is.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/is.json index 080c0baca2..15dfcff12e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/is.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/is.json @@ -1,6 +1,7 @@ { "culture": "is", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Velkomin", "Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", "Menu:Home": "Heim" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/it.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/it.json index 57c3cf63d4..6d695dd3dc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/it.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/it.json @@ -1,6 +1,7 @@ { "culture": "it", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Benvenuto", "Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/nl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/nl.json index 525ee02d10..dcf8345459 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/nl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/nl.json @@ -1,6 +1,7 @@ { "culture": "nl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Welkom", "Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/pl-PL.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/pl-PL.json index 2ebf70a5e1..9b582507fd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/pl-PL.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/pl-PL.json @@ -1,6 +1,7 @@ { "culture": "pl-PL", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Witaj", "Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", "Menu:Home": "Home" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/pt-BR.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/pt-BR.json index 96e45d3c3e..a60de1cd33 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/pt-BR.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/pt-BR.json @@ -1,6 +1,7 @@ { "culture": "pt-BR", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Seja bem-vindo!", "Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", "Menu:Home": "Principal" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ro-RO.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ro-RO.json index 48102a75b7..050b6feca6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ro-RO.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ro-RO.json @@ -1,6 +1,7 @@ { "culture": "ro-RO", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bun venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Acasă" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ru.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ru.json index eb4cc96b5d..263b7abc47 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ru.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Bine ati venit", "Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", "Menu:Home": "Главная" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/sk.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/sk.json index 227d0a74cb..1df924082b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/sk.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/sk.json @@ -1,6 +1,7 @@ { "culture": "sk", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Vitajte", "Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/sl.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/sl.json index 29876afa0c..dcf0a7d293 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/sl.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Dobrodošli", "Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", "Menu:Home": "Domov" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/tr.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/tr.json index 6cbdac76a2..5b5700b716 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/tr.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Hoşgeldiniz", "Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", "Menu:Home": "Ana sayfa" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/vi.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/vi.json index f2e55bd7b0..d738315efd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/vi.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "Chào mừng bạn", "Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", "Menu:Home": "Trang chủ" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/zh-Hans.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/zh-Hans.json index a43366df48..0d42020ab0 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/zh-Hans.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/zh-Hans.json @@ -1,8 +1,9 @@ { "culture": "zh-Hans", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "欢迎", "Welcome_Text": "这是 ABP 框架的极简单层应用程序启动模板。", "Menu:Home": "首页" } -} \ No newline at end of file +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/zh-Hant.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/zh-Hant.json index 7b4872c974..d00e97809c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/zh-Hant.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Localization/MyProjectName/zh-Hant.json @@ -1,6 +1,7 @@ { "culture": "zh-Hant", "texts": { + "AppName": "MyProjectName", "Welcome_Title": "歡迎", "Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", "Menu:Home": "首頁" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameBrandingProvider.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameBrandingProvider.cs index fb6e75d065..26f6ca9fff 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameBrandingProvider.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameGlobalFeatureConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameGlobalFeatureConfigurator.cs new file mode 100644 index 0000000000..40b41df34d --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameGlobalFeatureConfigurator.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameGlobalFeatureConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + /* You can configure (enable/disable) global features of the used modules here. + * Please refer to the documentation to learn more about the Global Features System: + * https://docs.abp.io/en/abp/latest/Global-Features + */ + }); + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModule.cs index 668e5eb26b..10313aaa85 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModule.cs @@ -142,6 +142,10 @@ public class MyProjectNameModule : AbpModule serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "00000000-0000-0000-0000-000000000000"); }); } + + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + MyProjectNameEfCoreEntityExtensionMappings.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModuleExtensionConfigurator.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModuleExtensionConfigurator.cs new file mode 100644 index 0000000000..37d6056f67 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModuleExtensionConfigurator.cs @@ -0,0 +1,68 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName; + +public static class MyProjectNameModuleExtensionConfigurator +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + ConfigureExistingProperties(); + ConfigureExtraProperties(); + }); + } + + private static void ConfigureExistingProperties() + { + /* You can change max lengths for properties of the + * entities defined in the modules used by your application. + * + * Example: Change user and role name max lengths + + AbpUserConsts.MaxNameLength = 99; + IdentityRoleConsts.MaxNameLength = 99; + + * Notice: It is not suggested to change property lengths + * unless you really need it. Go with the standard values wherever possible. + * + * If you are using EF Core, you will need to run the add-migration command after your changes. + */ + } + + private static void ConfigureExtraProperties() + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to define these extra properties + * with a high level, easy to use API. + * + * Example: Add a new property to the user entity of the identity module + + ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty<string>( //property type: string + "SocialSecurityNumber", //property name + property => + { + //validation rules + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); + + //...other configurations for this property + } + ); + }); + }); + + * See the documentation for more: + * https://docs.abp.io/en/abp/latest/Module-Entity-Extensions + */ + } +} diff --git a/templates/app/angular/src/app/app.module.ts b/templates/app/angular/src/app/app.module.ts index e09d7502fe..de45468f4a 100644 --- a/templates/app/angular/src/app/app.module.ts +++ b/templates/app/angular/src/app/app.module.ts @@ -1,12 +1,19 @@ -import { AccountConfigModule } from '@abp/ng.account/config'; -import { CoreModule } from '@abp/ng.core'; +import { CoreModule, provideAbpCore, withOptions } from '@abp/ng.core'; import { registerLocale } from '@abp/ng.core/locale'; -import { IdentityConfigModule } from '@abp/ng.identity/config'; -import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; -import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; +import { + InternetConnectionStatusComponent, + ThemeSharedModule, + provideAbpThemeShared, +} from '@abp/ng.theme.shared'; +import { provideFeatureManagementConfig } from '@abp/ng.feature-management'; +import { provideAbpOAuth } from '@abp/ng.oauth'; +import { provideIdentityConfig } from '@abp/ng.identity/config'; +import { provideSettingManagementConfig } from '@abp/ng.setting-management/config'; +import { provideTenantManagementConfig } from '@abp/ng.tenant-management/config'; +import { provideAccountConfig } from '@abp/ng.account/config'; import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x'; import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts'; -import { InternetConnectionStatusComponent, ThemeSharedModule } from '@abp/ng.theme.shared'; +import { AccountLayoutModule } from '@abp/ng.theme.lepton-x/account'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -14,32 +21,36 @@ import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { APP_ROUTE_PROVIDER } from './route.provider'; -import { FeatureManagementModule } from '@abp/ng.feature-management'; -import { AbpOAuthModule } from '@abp/ng.oauth'; -import { AccountLayoutModule } from '@abp/ng.theme.lepton-x/account'; + @NgModule({ imports: [ BrowserModule, BrowserAnimationsModule, AppRoutingModule, - CoreModule.forRoot({ - environment, - registerLocaleFn: registerLocale(), - }), - AbpOAuthModule.forRoot(), - ThemeSharedModule.forRoot(), - AccountLayoutModule.forRoot(), - AccountConfigModule.forRoot(), - IdentityConfigModule.forRoot(), - TenantManagementConfigModule.forRoot(), - SettingManagementConfigModule.forRoot(), + CoreModule, + ThemeSharedModule, ThemeLeptonXModule.forRoot(), SideMenuLayoutModule.forRoot(), - FeatureManagementModule.forRoot(), - InternetConnectionStatusComponent + AccountLayoutModule.forRoot(), + InternetConnectionStatusComponent, ], declarations: [AppComponent], - providers: [APP_ROUTE_PROVIDER], + providers: [ + APP_ROUTE_PROVIDER, + provideAbpCore( + withOptions({ + environment, + registerLocaleFn: registerLocale(), + }) + ), + provideAbpOAuth(), + provideAbpThemeShared(), + provideSettingManagementConfig(), + provideAccountConfig(), + provideIdentityConfig(), + provideTenantManagementConfig(), + provideFeatureManagementConfig(), + ], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameBrandingProvider.cs index 340ce5fb7b..314ea9a136 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Ui.Branding; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.Ui.Branding; using Volo.Abp.DependencyInjection; namespace MyCompanyName.MyProjectName; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyProjectNameBrandingProvider.cs index 3773a9b7e1..4095cbe1bf 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName.Blazor.Client; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Blazor.Client; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBrandingProvider.cs index d481c41c40..b995f4c8f9 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName.Blazor.Server.Tiered; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Tiered; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBrandingProvider.cs index c78d470cdc..ae49aeedbb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName.Blazor.Server; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Blazor.Server; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyProjectNameBrandingProvider.cs index ef4cfcedf8..dc3877c90d 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName.Blazor.WebApp.Client; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Blazor.WebApp.Client; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyProjectNameBrandingProvider.cs index 81c841dee7..fcd5567c23 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyProjectNameBrandingProvider.cs index 2f5f55a3a4..f0c27cce16 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName.Blazor.WebApp.Tiered; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Blazor.WebApp.Tiered; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyProjectNameBrandingProvider.cs index 1b37947e44..53719c33ec 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName.Blazor.WebApp; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Blazor.WebApp; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer<MyProjectNameResource> _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer<MyProjectNameResource> localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/App.razor b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/App.razor index f55ac673d4..97081ce51d 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/App.razor +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/App.razor @@ -7,9 +7,9 @@ <title>MyCompanyName.MyProjectName.Blazor - - - + + + @@ -35,8 +35,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ar.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ar.json index aa3bcfacb9..ef02e7fd37 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ar.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ar.json @@ -1,8 +1,9 @@ { "culture": "ar", "texts": { + "AppName": "MyProjectName", "Menu:Home": "الصفحة الرئيسية", "Welcome": "مرحباً", "LongWelcomeMessage": "مرحبا بكم في التطبيق. هذا مشروع بدء تشغيل يعتمد على إطار عمل ABP. لمزيد من المعلومات ، يرجى زيارة abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/cs.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/cs.json index 5a0bbf613e..a0a033ba9f 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/cs.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Úvod", "Welcome": "Vítejte", "LongWelcomeMessage": "Vítejte v aplikaci. Toto je startovací projekt založený na ABP frameworku. Pro více informací, navštivte abp.io." diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/de.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/de.json index 831493be86..ff1841c61d 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/de.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/de.json @@ -1,8 +1,9 @@ { "culture": "de", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Home", "Welcome": "Willkommen", "LongWelcomeMessage": "Willkommen bei der Anwendung. Dies ist ein Startup-Projekt, das auf dem ABP-Framework basiert. Weitere Informationen finden Sie unter abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en-GB.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en-GB.json index d2ca0793a3..016d7bd805 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en-GB.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en-GB.json @@ -1,8 +1,9 @@ { "culture": "en-GB", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Home", "Welcome": "Welcome", "LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json index d2a6a9831e..efa1c7a975 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Home", "Welcome": "Welcome", "LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io." diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/es.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/es.json index 31b4b59e25..8997c26b09 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/es.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/es.json @@ -1,8 +1,9 @@ { "culture": "es", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Inicio", "Welcome": "Bienvenido", "LongWelcomeMessage": "Bienvenido a la aplicación, este es un proyecto base basado en el framework ABP. Para más información, visita abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/fi.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/fi.json index a318859f23..f1670b26a0 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/fi.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/fi.json @@ -1,8 +1,9 @@ { "culture": "fi", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Koti", "Welcome": "Tervetuloa", "LongWelcomeMessage": "Tervetuloa sovellukseen. Tämä on ABP-kehykseen perustuva käynnistysprojekti. Lisätietoja on osoitteessa abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/fr.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/fr.json index e76eac0c71..b6c93cf3ba 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/fr.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/fr.json @@ -1,8 +1,9 @@ { "culture": "fr", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Accueil", "Welcome": "Bienvenue", "LongWelcomeMessage": "Bienvenue dans l'application. Il s'agit d'un projet de démarrage basé sur le framework ABP. Pour plus d'informations, visitez abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hi.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hi.json index a1676bfd45..5b8018decb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hi.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hi.json @@ -1,8 +1,9 @@ { "culture": "hi", "texts": { + "AppName": "MyProjectName", "Menu:Home": "घर", "Welcome": "स्वागत हे", "LongWelcomeMessage": "आवेदन करने के लिए आपका स्वागत है। यह एबीपी ढांचे पर आधारित एक स्टार्टअप परियोजना है। अधिक जानकारी के लिए, abp.io पर जाएं।" } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hr.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hr.json index fa8efab322..04987512bd 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hr.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hr.json @@ -1,6 +1,7 @@ { "culture": "hr", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Početna", "Welcome": "Dobrodošli", "LongWelcomeMessage": "Dobrodošli u aplikaciju. Ovo je startup projekt temeljen na ABP framework-u. Za više informacija posjetite abp.io." diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hu.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hu.json index c7b6a33a0f..bbb72dec70 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hu.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/hu.json @@ -1,8 +1,9 @@ { "culture": "hu", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Kezdőlap", "Welcome": "Üdvözlöm", "LongWelcomeMessage": "Üdvözöljük az alkalmazásban. Ez egy ABP keretrendszeren alapuló startup projekt. További információkért látogasson el az abp.io oldalra." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/is.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/is.json index 190df90373..95ec05d242 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/is.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/is.json @@ -1,8 +1,9 @@ { "culture": "is", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Heim", "Welcome": "Velkomin", "LongWelcomeMessage": "Verið velkomin í forritið. Þetta er startup verkefni sem byggir á ABP. Nánari upplýsingar er að finna á abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/it.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/it.json index 82ce42b033..2e41668e60 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/it.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/it.json @@ -1,8 +1,9 @@ { "culture": "it", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Home", "Welcome": "Benvenuto", "LongWelcomeMessage": "Benvenuto nell'applicazione. Questo è un progetto di avvio basato sul framework ABP. Per ulteriori informazioni, visita abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/nl.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/nl.json index 9ba8da4743..65cc59afd9 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/nl.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/nl.json @@ -1,8 +1,9 @@ { "culture": "nl", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Home", "Welcome": "Welkom", "LongWelcomeMessage": "Welkom bij de applicatie. Dit is een startup-project gebaseerd op het ABP-framework. Bezoek abp.io voor meer informatie." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json index 33412f307c..3407455f4f 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json @@ -1,8 +1,9 @@ { "culture": "pl-PL", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Home", "Welcome": "Witaj", "LongWelcomeMessage": "Witaj w aplikacji. To jest inicjalny projekt bazujący na ABP framework. Po więcej informacji odwiedź stronę abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pt-BR.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pt-BR.json index 8c818a07af..aa093383ff 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pt-BR.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pt-BR.json @@ -1,8 +1,9 @@ { "culture": "pt-BR", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Principal", "Welcome": "Seja bem-vindo!", "LongWelcomeMessage": "Bem-vindo a esta aplicação. Este é um projeto inicial baseado no ABP framework. Para mais informações, visite abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ro-RO.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ro-RO.json index 1fe560196e..e57cb2d0ea 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ro-RO.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ro-RO.json @@ -1,8 +1,9 @@ { "culture": "ro-RO", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Acasă", "Welcome": "Bun venit", "LongWelcomeMessage": "Bun venit la aplicaţie. Acesta este un proiect de pornire bazat pe framework-ul ABP. Pentru mai multe informaţii, vizitaţi, visit abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ru.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ru.json index 8464e44344..bcd0b9495c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ru.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Главная", "Welcome": "Добро пожаловать", "LongWelcomeMessage": "Добро пожаловать в приложение. Этот запущенный проект основан на фреймворке ABP. Для получения дополнительной информации посетите сайт abp.io." diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sk.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sk.json index 4f35aaf1c3..f824e7ad43 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sk.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sk.json @@ -1,8 +1,9 @@ { "culture": "sk", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Domov", "Welcome": "Vitajte", "LongWelcomeMessage": "Vitajte v aplikácii. Toto je štartovací projekt založený na ABP frameworku. Viac informácií nájdete na stránke abp.io." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json index a066ef26ba..2de111e3bd 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Domov", "Welcome": "Dobrodošli", "LongWelcomeMessage": "Dobrodošli v aplikaciji. To je začetni projekt na osnovi okolja ABP. Za več informacij obiščite abp.io." diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json index 2cc911e48e..6c31461130 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json @@ -1,8 +1,9 @@ { "culture": "tr", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Ana sayfa", "Welcome": "Hoşgeldiniz", "LongWelcomeMessage": "Uygulamaya hoşgeldiniz. Bu, ABP framework'ü üzerine bina edilmiş bir başlangıç projesidir. Daha fazla bilgi için abp.io adresini ziyaret edebilirsiniz." } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/vi.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/vi.json index c115a35726..805832d4fa 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/vi.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "AppName": "MyProjectName", "Menu:Home": "Trang chủ", "Welcome": "Chào mừng bạn", "LongWelcomeMessage": "Chào mừng bạn đến ứng dụng. Đây là một dự án khởi nghiệp dựa trên khung ABP. Để biết thêm thông tin, hãy truy cập abp.io." diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json index 65828f39b2..4696cc90a5 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json @@ -1,8 +1,9 @@ { "culture": "zh-Hans", "texts": { + "AppName": "MyProjectName", "Menu:Home": "首页", "Welcome": "欢迎", "LongWelcomeMessage": "欢迎使用本应用程序。这是一个基于 ABP 框架的启动项目。更多信息,请访问 abp.io。" } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json index 31e0ab5a47..37717c59fc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json @@ -1,8 +1,9 @@ { "culture": "zh-Hant", "texts": { + "AppName": "MyProjectName", "Menu:Home": "首頁", "Welcome": "歡迎", "LongWelcomeMessage": "歡迎來到此應用程式. 這是一個基於ABP框架的起始專案. 有關更多訊息, 請瀏覽 abp.io." } - } \ No newline at end of file + } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameBrandingProvider.cs index fb6e75d065..26f6ca9fff 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.DependencyInjection; using Volo.Abp.Ui.Branding; namespace MyCompanyName.MyProjectName; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameBrandingProvider.cs index 5263e0a4f5..7dc70e7800 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Ui.Branding; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.Ui.Branding; using Volo.Abp.DependencyInjection; namespace MyCompanyName.MyProjectName.Web; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Web; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameBrandingProvider.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameBrandingProvider.cs index 5263e0a4f5..7dc70e7800 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameBrandingProvider.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameBrandingProvider.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Ui.Branding; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.Ui.Branding; using Volo.Abp.DependencyInjection; namespace MyCompanyName.MyProjectName.Web; @@ -6,5 +8,12 @@ namespace MyCompanyName.MyProjectName.Web; [Dependency(ReplaceServices = true)] public class MyProjectNameBrandingProvider : DefaultBrandingProvider { - public override string AppName => "MyProjectName"; + private IStringLocalizer _localizer; + + public MyProjectNameBrandingProvider(IStringLocalizer localizer) + { + _localizer = localizer; + } + + public override string AppName => _localizer["AppName"]; } diff --git a/templates/module/angular/projects/dev-app/src/app/app.module.ts b/templates/module/angular/projects/dev-app/src/app/app.module.ts index 615bc75f2c..ab0a3acb67 100644 --- a/templates/module/angular/projects/dev-app/src/app/app.module.ts +++ b/templates/module/angular/projects/dev-app/src/app/app.module.ts @@ -1,11 +1,13 @@ -import { AccountConfigModule } from '@abp/ng.account/config'; -import { CoreModule } from '@abp/ng.core'; +import { provideAccountConfig } from '@abp/ng.account/config'; +import { CoreModule, provideAbpCore, withOptions } from '@abp/ng.core'; import { registerLocale } from '@abp/ng.core/locale'; -import { IdentityConfigModule } from '@abp/ng.identity/config'; -import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; -import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; -import { ThemeBasicModule } from '@abp/ng.theme.basic'; -import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { provideIdentityConfig } from '@abp/ng.identity/config'; +import { provideSettingManagementConfig } from '@abp/ng.setting-management/config'; +import { provideTenantManagementConfig } from '@abp/ng.tenant-management/config'; +import { ThemeBasicModule, provideThemeBasicConfig } from '@abp/ng.theme.basic'; +import { ThemeSharedModule, provideAbpThemeShared } from '@abp/ng.theme.shared'; +import { provideFeatureManagementConfig } from '@abp/ng.feature-management'; +import { provideAbpOAuth } from '@abp/ng.oauth'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -14,31 +16,36 @@ import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { APP_ROUTE_PROVIDER } from './route.provider'; -import { FeatureManagementModule } from '@abp/ng.feature-management'; -import { AbpOAuthModule } from '@abp/ng.oauth'; @NgModule({ imports: [ BrowserModule, BrowserAnimationsModule, AppRoutingModule, - CoreModule.forRoot({ - environment, - registerLocaleFn: registerLocale(), - sendNullsAsQueryParam: false, - skipGetAppConfiguration: false, - }), - AbpOAuthModule.forRoot(), - ThemeSharedModule.forRoot(), - AccountConfigModule.forRoot(), - IdentityConfigModule.forRoot(), - TenantManagementConfigModule.forRoot(), - SettingManagementConfigModule.forRoot(), + CoreModule, + ThemeSharedModule, MyProjectNameConfigModule.forRoot(), - ThemeBasicModule.forRoot(), - FeatureManagementModule.forRoot(), + ThemeBasicModule, + ], + providers: [ + APP_ROUTE_PROVIDER, + provideAbpCore( + withOptions({ + environment, + registerLocaleFn: registerLocale(), + sendNullsAsQueryParam: false, + skipGetAppConfiguration: false, + }) + ), + provideAbpOAuth(), + provideAbpThemeShared(), + provideSettingManagementConfig(), + provideAccountConfig(), + provideIdentityConfig(), + provideTenantManagementConfig(), + provideFeatureManagementConfig(), + provideThemeBasicConfig(), ], - providers: [APP_ROUTE_PROVIDER], declarations: [AppComponent], bootstrap: [AppComponent], }) diff --git a/templates/module/angular/projects/dev-app/src/index.html b/templates/module/angular/projects/dev-app/src/index.html index 067936da71..88a791e8e4 100644 --- a/templates/module/angular/projects/dev-app/src/index.html +++ b/templates/module/angular/projects/dev-app/src/index.html @@ -1,13 +1,13 @@ - + - - - DevApp - - - - - - - + + + MyProjectName + + + + + + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/App.razor b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/App.razor index 9371e0b3f4..4fa5c160fb 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/App.razor +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/App.razor @@ -7,9 +7,9 @@ MyCompanyName.MyProjectName.Blazor - - - + + + @@ -29,8 +29,8 @@ - - + +