Browse Source

Merge branch 'dev' into auto-merge/rel-8-2/2746

pull/19914/head
Engincan VESKE 2 years ago
committed by GitHub
parent
commit
e95305a926
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      Directory.Packages.props
  2. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json
  3. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
  4. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json
  5. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json
  6. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  7. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json
  8. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json
  9. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json
  10. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json
  11. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json
  12. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
  13. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json
  14. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json
  15. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json
  16. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json
  17. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json
  18. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json
  19. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json
  20. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json
  21. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
  22. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json
  23. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
  24. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json
  25. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json
  26. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json
  27. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json
  28. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  29. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json
  30. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json
  31. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json
  32. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json
  33. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json
  34. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json
  35. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json
  36. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json
  37. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json
  38. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json
  39. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json
  40. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json
  41. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json
  42. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json
  43. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json
  44. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json
  45. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
  46. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json
  47. 4
      common.props
  48. BIN
      docs/en/Blog-Posts/2024-05 ABP Unification/abp-studio-solution-runner.png
  49. BIN
      docs/en/Blog-Posts/2024-05 ABP Unification/cover.png
  50. BIN
      docs/en/Blog-Posts/2024-05 ABP Unification/docs-new-navigation.png
  51. BIN
      docs/en/Blog-Posts/2024-05 ABP Unification/docs-project-selection.png
  52. BIN
      docs/en/Blog-Posts/2024-05 ABP Unification/new-get-started.png
  53. BIN
      docs/en/Blog-Posts/2024-05 ABP Unification/new-mega-menu.png
  54. BIN
      docs/en/Blog-Posts/2024-05 ABP Unification/new-pricing.png
  55. 111
      docs/en/Blog-Posts/2024-05 ABP Unification/post.md
  56. BIN
      docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/images/blob-storage.png
  57. 114
      docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/post.md
  58. 201
      docs/en/Community-Articles/2024-05-01-How-ABP-get-current-user/POST.md
  59. 85
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/POST.md
  60. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/access-abp-framework-mvc-project.png
  61. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/add-environment-variables.png
  62. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-container-abp.png
  63. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-resources.png
  64. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-sql-database-connection-strings.png
  65. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/build-docker-image.png
  66. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/check-job-status.png
  67. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps-job.png
  68. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps.png
  69. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/deploy-docker-image.png
  70. BIN
      docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/docker-hub-repository.png
  71. BIN
      docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/adding-incorrect-data.png
  72. BIN
      docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/error.png
  73. BIN
      docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/validation.png
  74. 115
      docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/post.md
  75. BIN
      docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/automapper.png
  76. BIN
      docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger1.png
  77. BIN
      docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger2.png
  78. 134
      docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/post.md
  79. 340
      docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/POST.md
  80. BIN
      docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/cover-image.png
  81. BIN
      docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/demo.gif
  82. BIN
      docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis-steps.png
  83. BIN
      docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis.png
  84. 59
      docs/en/UI/Angular/Title-Strategy-Service.md
  85. 4
      docs/en/docs-nav.json
  86. 3
      docs/pt-BR/AspNetCore/Auto-API-Controllers.md
  87. 3
      docs/pt-BR/AspNetCore/Bundling-Minification.md
  88. 3
      docs/pt-BR/AspNetCore/Client-Side-Package-Management.md
  89. 3
      docs/pt-BR/AspNetCore/Dynamic-CSharp-API-Clients.md
  90. 3
      docs/pt-BR/AspNetCore/Tag-Helpers/Dynamic-Forms.md
  91. 3
      docs/pt-BR/AspNetCore/Tag-Helpers/Index.md
  92. 3
      docs/pt-BR/AspNetCore/Theming.md
  93. 3
      docs/pt-BR/AspNetCore/Widgets.md
  94. 391
      docs/pt-BR/Audit-Logging.md
  95. 360
      docs/pt-BR/Authorization.md
  96. 159
      docs/pt-BR/Background-Jobs-Quartz.md
  97. 146
      docs/pt-BR/Background-Workers-Quartz.md
  98. 144
      docs/pt-BR/Background-Workers.md
  99. 343
      docs/pt-BR/Exception-Handling.md
  100. 80
      docs/pt-BR/Modules/Account.md

10
Directory.Packages.props

@ -16,10 +16,10 @@
<PackageVersion Include="AWSSDK.SecurityToken" Version="3.7.300.2" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.17.0" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.19.1" />
<PackageVersion Include="Blazorise" Version="1.5.0" />
<PackageVersion Include="Blazorise.Components" Version="1.5.0" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.5.0" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.5.0" />
<PackageVersion Include="Blazorise" Version="1.5.2" />
<PackageVersion Include="Blazorise.Components" Version="1.5.2" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.5.2" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.5.2" />
<PackageVersion Include="Castle.Core" Version="5.1.1" />
<PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />
@ -174,4 +174,4 @@
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" />
<PackageVersion Include="Fody" Version="6.8.0" />
</ItemGroup>
</Project>
</Project>

3
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."
}
}

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

@ -816,6 +816,7 @@
"ErrorExceptionMessage": "حدث خطأ أثناء معالجة طلبك",
"WatchTakeCodeGeneration": "شاهد فيديو \"استكشاف إمكانات إنشاء الأكواد البرمجية: ABP Suite\"!",
"ExtendNow": "تمديد / تجديد",
"RegisterDemo": "يسجل"
"RegisterDemo": "يسجل",
"AbpStudio_ComingSoon": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">إذا كنت ترغب في اختبار ABP Studio قبل أي شخص آخر، يمكنك أن تصبح أحد مختبري النسخة التجريبية</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Pokud chcete ABP Studio otestovat dříve než kdokoli jiný, staňte se BETA testerem</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Wenn Sie ABP Studio vor allen anderen testen möchten, werden Sie BETA-Tester</span>"
}
}

2
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 <span class=\"text-highlight-white\">cross-platform desktop application</span> for ABP developers.",
"AbpStudio_Description2": "It is <span class=\"text-highlight-white\">well integrated</span> to the ABP Framework and aims to provide a comfortable development environment for you by <span class=\"text-highlight-white\">automating things, providing insights</span> about your solution, making <span class=\"text-highlight-white\">develop, run</span> and <span class=\"text-highlight-white\">deploy</span> your solutions much easier.",
"AbpStudio_ComingSoon": "Coming Soon <span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Planned beta release date: Q4 of 2023.</span>",
"AbpStudio_ComingSoon": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">If you want to test ABP Studio before anyone else, become a BETA tester</span>",
"AbpStudio_PlannedPreviewDate": "Planned preview release date: Q4 of 2023.",
"BetaRequest": "Beta Request",
"CreateNewSolutions": "Create New Solutions",

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Si quieres probar ABP Studio antes que nadie, conviértete en tester BETA</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Jos haluat testata ABP Studiota ennen muita, ryhdy BETA-testaajaksi</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Si vous souhaitez tester ABP Studio avant tout le monde, devenez BETA testeur</span>"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json

@ -430,6 +430,7 @@
"ErrorExceptionMessage": "आपका अनुरोध संसाधित करते समय एक त्रुटि पाई गई",
"WatchTakeCodeGeneration": "\"कोड जनरेशन की क्षमता का अन्वेषण करें: एबीपी सुइट\" वीडियो देखें!",
"ExtendNow": "विस्तार/नवीनीकरण",
"RegisterDemo": "पंजीकरण करवाना"
"RegisterDemo": "पंजीकरण करवाना",
"AbpStudio_ComingSoon": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">यदि आप किसी और से पहले एबीपी स्टूडियो का परीक्षण करना चाहते हैं, तो बीटा परीक्षक बनें</span>"
}
}

4
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 <span class=\"text-highlight-white\">stolna aplikacija za više platformi</span> za ABP programere.",
"AbpStudio_Description2": "<span class=\"text-highlight-white\">Dobro je integriran</span> u ABP Framework i ima za cilj pružiti vam ugodno razvojno okruženje <span class=\"text-highlight-white\">automatiziranjem stvari, pružanjem uvida</span> u vaše rješenje, čineći <span class=\"text-highlight-white\">razvoj, pokretanje</span> i <span class=\"text-highlight-white\">implementaciju</span> vaših rješenja puno lakšim.",
"AbpStudio_ComingSoon": "Uskoro. <span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Planirani datum izdavanja beta verzije: 4. tromjesečje 2023.</span>",
"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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Ako želite testirati ABP Studio prije svih ostalih, postanite BETA tester</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Ha bárki más előtt szeretné tesztelni az ABP Studio-t, legyen BÉTA tesztelő</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Ef þú vilt prófa ABP Studio á undan öllum öðrum skaltu gerast BETA prófari</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Se vuoi provare ABP Studio prima di chiunque altro, diventa un BETA tester</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Als u ABP Studio als eerste wilt testen, wordt dan BETA-tester</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Jeśli chcesz przetestować ABP Studio zanim ktokolwiek inny, zostań testerem BETA</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Se você quiser testar o ABP Studio antes de todo mundo, torne-se um testador BETA</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Dacă doriți să testați ABP Studio înaintea oricui, deveniți un tester BETA</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Ak chcete otestovať ABP Studio skôr ako ktokoľvek iný, staňte sa BETA testerom</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Če želite preizkusiti ABP Studio pred vsemi drugimi, postanite BETA tester</span>"
}
}

4
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 <span class=\"text-highlight-white\">çok platformlu masaüstü uygulamasıdır</span>.",
"AbpStudio_Description2": "ABP Studio, ABP Framework'e <span class=\"text-highlight-white\">iyi entegre edilmiştir</span> ve sizin için işleri <span class=\"text-highlight-white\">otomatikleştirerek, projeniz hakkında bilgi sağlayarak</span>, projelerinizi <span class=\"text-highlight-white\">geliştirmeyi, çalıştırmayı</span> ve <span class=\"text-highlight-white\">dağıtımını</span> çok daha kolay hale getirmeyi amaçlamaktadır.",
"AbpStudio_ComingSoon": "Yakında <span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">Planlanan beta sürüm tarihi: 2023'ün 4. çeyreği.</span>",
"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 <a href='mailto:sales@volosoft.com'>sales@volosoft.com</a> adresinden bizimle iletişime geçin veya iletişim sayfasına gitmek için <a href='https://commercial.abp.io/contact'>buraya</a> tıklayın."
"LinkExpiredMessage": "Ödeme bağlantısının süresi doldu! Bağlantıyı güncellemek için <a href='mailto:sales@volosoft.com'>sales@volosoft.com</a> adresinden bizimle iletişime geçin veya iletişim sayfasına gitmek için <a href='https://commercial.abp.io/contact'>buraya</a> tıklayın.",
"AbpStudio_ComingSoon": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">ABP Studio'yu herkesten önce test etmek istiyorsanız BETA test kullanıcısı olun</span>"
}
}

3
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": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">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</span>"
}
}

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

@ -915,7 +915,6 @@
"AbpStudio_Description": "ABP Studio 仍在开发中。您可以填写下面的表单,成为首批用户之一。",
"AbpStudio_Description1": "ABP Studio 是专为 ABP 开发人员设计的<span class=\"text-highlight-white\">跨平台桌面应用程序</span>。",
"AbpStudio_Description2": "它<span class=\"text-highlight-white\">与 ABP 框架集成得很好</span>,旨在通过<span class=\"text-highlight-white\">自动化、提供有关解决方案的见解</span>,为您提供舒适的开发环境,使<span class=\"text-highlight-white\">开发、运行</span>和<span class=\"text-highlight-white\">部署</span>解决方案变得更加容易。",
"AbpStudio_ComingSoon": "即将发布 <span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">计划测试版发布日期:</span>计划测试版发布日期:2023年第四季度。",
"AbpStudio_PlannedPreviewDate": "计划预览版发布日期:2023 年第四季度。",
"BetaRequest": "测试版请求",
"CreateNewSolutions": "创建新的解决方案",
@ -1110,6 +1109,7 @@
"ErrorExceptionMessage": "处理您的请求时发生错误",
"WatchTakeCodeGeneration": "观看“探索代码生成的潜力:ABP Suite”视频!",
"PreBuiltApplicationModulesTitle": "预构建应用程序 <span class=\"gradient-commercial\">模块</span>",
"RegisterDemo": "登记"
"RegisterDemo": "登记",
"AbpStudio_ComingSoon": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">如果您想先于其他人测试 ABP Studio,请成为 BETA 测试员</span>"
}
}

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

@ -897,7 +897,6 @@
"AbpStudio_Description": "ABP Studio 仍在開發中。您可以填寫下面的表单,成為第一批使用者之一。",
"AbpStudio_Description1": "ABP Studio 是一款針對 ABP 開發人員的<span class=\"text-highlight-white\">跨平台桌面應用程式</span>。",
"AbpStudio_Description2": "它與 ABP 框架<span class=\"text-highlight-white\">很好地集成</span>,旨在透過<span class=\"text-highlight-white\">自動化為您提供一個舒適的開發環境,提供有關您的解決方案的見解</span>,使<span class=\"text-highlight-white\">開發、運行</span>和<span class=\"text-highlight-white\">部署</span>您的解決方案變得更加容易。",
"AbpStudio_ComingSoon": "即將推出<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">計劃測試版發布日期:2023 年第四季。</span>",
"AbpStudio_PlannedPreviewDate": "計畫預覽版發布日期:2023 年第四季。",
"BetaRequest": "測試版請求",
"CreateNewSolutions": "建立新的解決方案",
@ -1100,6 +1099,7 @@
"WatchTakeCodeGeneration": "观看“探索代码生成的潜力:ABP Suite”视频!",
"ExtendNow": "延长 / 续订",
"PreBuiltApplicationModulesTitle": "预构建应用程序 <span class=\"gradient-commercial\">模块</span>",
"RegisterDemo": "登记"
"RegisterDemo": "登记",
"AbpStudio_ComingSoon": "<span class=\"opacity-50 ms-0 ms-md-3 d-block d-md-inline-block \">如果您想先于其他人测试 ABP Studio,请成为 BETA 测试员</span>"
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> أيد.",
"Preview": "معاينة",
"VisitVideoCourseDescription": "إذا كنت ترغب في تعلم أساسيات إطار عمل برنامج ABP، فاطلع على دورات الفيديو الخاصة ببرنامج ABP Essentials.",
"VisitPage": "زر الصفحة"
"VisitPage": "زر الصفحة",
"ConfirmEmailForPost": "لتتمكن من النشر، تحتاج إلى تأكيد بريدك الإلكتروني. انتقل إلى account.abp.io/Account/Manage وتحقق من بريدك الإلكتروني في علامة التبويب \"المعلومات الشخصية\".",
"DailyPostCreateLimitation": "لقد وصلت إلى الحد الأقصى اليومي لإنشاء المنشورات. يمكنك إنشاء مشاركة جديدة في {0}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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."
}
}

4
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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> का समर्थन किया।",
"Preview": "पूर्व दर्शन",
"VisitPage": "यात्रा पेज",
"VisitVideoCourseDescription": "यदि आप एबीपी फ्रेमवर्क की मूल बातें सीखना चाहते हैं, तो एबीपी एसेंशियल वीडियो पाठ्यक्रम देखें।"
"VisitVideoCourseDescription": "यदि आप एबीपी फ्रेमवर्क की मूल बातें सीखना चाहते हैं, तो एबीपी एसेंशियल वीडियो पाठ्यक्रम देखें।",
"ConfirmEmailForPost": "पोस्ट करने में सक्षम होने के लिए, आपको अपने ईमेल की पुष्टि करनी होगी। account.abp.io/Account/Manage पर जाएं और व्यक्तिगत जानकारी टैब में अपना ईमेल सत्यापित करें।",
"DailyPostCreateLimitation": "आप दैनिक पोस्ट निर्माण सीमा तक पहुंच गए हैं। आप {0} में एक नई पोस्ट बना सकते हैं।"
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 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}."
}
}

4
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."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> đượ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}."
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 支持的。",
"Preview": "预览",
"VisitPage": "访问页面",
"VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。"
"VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。",
"ConfirmEmailForPost": "为了能够发帖,您需要确认您的电子邮件。转到 account.abp.io/Account/Manage 并在“个人信息”选项卡中验证您的电子邮件。",
"DailyPostCreateLimitation": "您已达到每日帖子创建限制。您可以在 {0} 中创建新帖子。"
}
}

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

@ -259,6 +259,8 @@
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 支持的。",
"Preview": "预览",
"VisitPage": "访问页面",
"VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。"
"VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。",
"ConfirmEmailForPost": "为了能够发帖,您需要确认您的电子邮件。转到 account.abp.io/Account/Manage 并在“个人信息”选项卡中验证您的电子邮件。",
"DailyPostCreateLimitation": "您已达到每日帖子创建限制。您可以在 {0} 中创建新帖子。"
}
}

4
common.props

@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>8.2.0-rc.3</Version>
<LeptonXVersion>3.2.0-rc.3</LeptonXVersion>
<Version>8.3.0-preview</Version>
<LeptonXVersion>3.3.0-preview</LeptonXVersion>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>

BIN
docs/en/Blog-Posts/2024-05 ABP Unification/abp-studio-solution-runner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
docs/en/Blog-Posts/2024-05 ABP Unification/cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
docs/en/Blog-Posts/2024-05 ABP Unification/docs-new-navigation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/Blog-Posts/2024-05 ABP Unification/docs-project-selection.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/en/Blog-Posts/2024-05 ABP Unification/new-get-started.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

BIN
docs/en/Blog-Posts/2024-05 ABP Unification/new-mega-menu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

BIN
docs/en/Blog-Posts/2024-05 ABP Unification/new-pricing.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

111
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.

BIN
docs/en/Community-Articles/2024-04-19-using-blob-storage-with-abp/images/blob-storage.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

114
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<ProfilePictureContainer> _blobContainer;
public ProfileAppService(IBlobContainer<ProfilePictureContainer> 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<byte[]> 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.

201
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<string, string> ClaimTypeMapping = new Dictionary<string, string>
{
{ "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.

85
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.

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/access-abp-framework-mvc-project.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/add-environment-variables.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-container-abp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-resources.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/azure-sql-database-connection-strings.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/build-docker-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/check-job-status.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps-job.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/create-azure-container-apps.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/deploy-docker-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

BIN
docs/en/Community-Articles/2024-05-07-Azure-Container-Apps-Deployment-with-ABP/docker-hub-repository.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/adding-incorrect-data.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/en/Community-Articles/2024-05-07-using-fluent-validation-with-abp/images/validation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

115
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<Guid>
{
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<ProductCreateDto>
{
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/) 

BIN
docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/automapper.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/en/Community-Articles/2024-05-09-what-is-object-to-object-mapping/images/swagger2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

134
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<CustomerDto> 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<CustomerDto> CreateAsync(CustomerCreateDto input)
{
var customer = await _customerManager.CreateAsync(
input.BirthDay, input.MembershipDate, input.FirstName, input.LastName
);
return ObjectMapper.Map<Customer, CustomerDto>(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<Guid>
{
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<PagedResultDto<CustomerGetDto>> 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<CustomerGetDto>
{
TotalCount = totalCount,
Items = ObjectMapper.Map<List<Customer>, List<CustomerGetDto>>(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<Customer>` to `List<CustomerGetDto>` 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<Customer, CustomerGetDto>();
}
}
````
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<Customer, CustomerGetDto>().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).

340
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<CmsKitCommentOptions>(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
<div class="container">
<h5 class="display-5">Comments:</h5>
@await Component.InvokeAsync(typeof(CommentingViewComponent), new
{
entityType = "Comment",
entityId = "SentimentAnalysisDemo",
isReadOnly = false
})
</div>
```
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<CmsKitCommentOptions> cmsCommentOptions,
ISpamDetector spamDetector
)
: base(commentRepository, cmsUserLookupService, distributedEventBus, commentManager, cmsCommentOptions)
{
SpamDetector = spamDetector;
}
public override async Task<CommentDto> 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<CommentDto> 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<SentimentAnalyzeInput>(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<SentimentAnalyzeInput, SentimentAnalyzeResult>(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 :)

BIN
docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

BIN
docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/demo.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

BIN
docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis-steps.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/Community-Articles/2024-05-10-Sentiment-Analysis-within-ABP-Based-Application/sentiment-analysis.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

59
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 **`<title> | <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`**.

4
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"
}
]
},

3
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)

3
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)

3
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)

3
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)

3
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)

3
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)

3
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)

3
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)

391
docs/pt-BR/Audit-Logging.md

@ -1,3 +1,390 @@
# Audit Logging
# Registro de Auditoria
Façam
[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.

360
docs/pt-BR/Authorization.md

@ -1,3 +1,359 @@
## Authorization
# Autorização
Façam
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>

159
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>

146
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.

144
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)

343
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)

80
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.

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

Loading…
Cancel
Save