Browse Source

Merge branch 'rel-4.0' of https://github.com/abpframework/abp into auto-merge/rel-3-3/5

pull/6781/head
muhammedaltug 5 years ago
parent
commit
e84fb06836
  1. 18
      .github/workflows/auto-pr.yml
  2. 2
      .github/workflows/build-and-test.yml
  3. 4
      .gitignore
  4. 7
      NuGet.Config
  5. 99
      README.md
  6. 14
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/de-DE.json
  7. 14
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/es.json
  8. 199
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/de-DE.json
  9. 199
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/es.json
  10. 33
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/de-DE.json
  11. 33
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/es.json
  12. 35
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de-DE.json
  13. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  14. 35
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json
  15. 90
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de-DE.json
  16. 17
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  17. 90
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json
  18. 189
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/de-DE.json
  19. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  20. 189
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/es.json
  21. 6
      build/common.ps1
  22. 11
      common.DotSettings
  23. 8
      common.props
  24. 35
      docs/en/API/Auto-API-Controllers.md
  25. 51
      docs/en/API/Dynamic-CSharp-API-Clients.md
  26. 6
      docs/en/Application-Services.md
  27. 2
      docs/en/AspNet-Boilerplate-Migration-Guide.md
  28. 31
      docs/en/Authentication/Social-External-Logins.md
  29. 24
      docs/en/Authorization.md
  30. 2
      docs/en/Background-Workers.md
  31. 8
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/Post.md
  32. 45
      docs/en/Blog-Posts/2020-10-15 v3_3_Preview/POST.md
  33. 47
      docs/en/Blog-Posts/2020-10-27 v3_3_Release_Stable/POST.md
  34. 143
      docs/en/Blog-Posts/2020-11-12 v4_0_Preview/POST.md
  35. BIN
      docs/en/Blog-Posts/2020-11-12 v4_0_Preview/abp-suite-auto-complete-dropdown.png
  36. 39
      docs/en/CLI.md
  37. 30
      docs/en/CSRF-Anti-Forgery.md
  38. 8
      docs/en/Community-Articles/2020-04-19-Customize-the-SignIn-Manager/POST.md
  39. 14
      docs/en/Community-Articles/2020-08-07-Passwordless-Authentication/POST.md
  40. 46
      docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/POST.md
  41. BIN
      docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/add-to-languages.png
  42. BIN
      docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/database-table.png
  43. BIN
      docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/language-target.png
  44. BIN
      docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/website-new-language.png
  45. 27
      docs/en/Contribution/Index.md
  46. 52
      docs/en/Customizing-Application-Modules-Overriding-Services.md
  47. 10
      docs/en/Data-Access.md
  48. 2
      docs/en/Data-Filtering.md
  49. 1978
      docs/en/Domain-Driven-Design-Implementation-Guide.md
  50. 14
      docs/en/Domain-Driven-Design.md
  51. 311
      docs/en/Entity-Framework-Core.md
  52. 10
      docs/en/Exception-Handling.md
  53. 2
      docs/en/Getting-Started-AspNetCore-Application.md
  54. 65
      docs/en/Getting-Started-Create-Solution.md
  55. 4
      docs/en/Getting-Started-React-Native.md
  56. 217
      docs/en/Getting-Started-Running-Solution.md
  57. 56
      docs/en/Getting-Started-Setup-Environment.md
  58. 301
      docs/en/Getting-Started.md
  59. 68
      docs/en/Index.md
  60. 4
      docs/en/Localization.md
  61. 121
      docs/en/Migration-Guides/Abp-4_0-Angular.md
  62. 88
      docs/en/Migration-Guides/Abp-4_0-Blazor.md
  63. 6
      docs/en/Migration-Guides/Abp-4_0-MVC-Razor-Pages.md
  64. 280
      docs/en/Migration-Guides/Abp-4_0.md
  65. 5
      docs/en/Migration-Guides/Index.md
  66. BIN
      docs/en/Migration-Guides/images/route-4.png
  67. BIN
      docs/en/Migration-Guides/images/route-before-4.png
  68. 392
      docs/en/Module-Entity-Extensions.md
  69. 37
      docs/en/Modules/Account.md
  70. 3
      docs/en/Modules/Client-Simulation.md
  71. 3
      docs/en/Modules/Cms-Kit.md
  72. 4
      docs/en/Modules/Docs.md
  73. 52
      docs/en/Modules/Identity.md
  74. 26
      docs/en/Modules/Index.md
  75. 47
      docs/en/Modules/Organization-Units.md
  76. 3
      docs/en/Modules/Users.md
  77. 16
      docs/en/MongoDB.md
  78. 3
      docs/en/Multi-Tenancy.md
  79. 233
      docs/en/PlugIn-Modules.md
  80. 8
      docs/en/Repositories.md
  81. 11
      docs/en/Road-Map.md
  82. 6
      docs/en/Specifications.md
  83. 2
      docs/en/Startup-Templates/Application.md
  84. 1
      docs/en/Startup-Templates/Index.md
  85. 27
      docs/en/Startup-Templates/WPF.md
  86. 3
      docs/en/Swagger.md
  87. 51
      docs/en/Tutorials/Part-10.md
  88. 5
      docs/en/Tutorials/Part-2.md
  89. 452
      docs/en/Tutorials/Part-3.md
  90. 6
      docs/en/Tutorials/Part-4.md
  91. 138
      docs/en/Tutorials/Part-5.md
  92. 17
      docs/en/Tutorials/Part-9.md
  93. BIN
      docs/en/Tutorials/images/blazor-bookstore-book-list.png
  94. BIN
      docs/en/Tutorials/images/blazor-delete-book-action.png
  95. BIN
      docs/en/Tutorials/images/blazor-edit-book-action-2.png
  96. 135
      docs/en/UI/Angular/Config-State-Service.md
  97. 193
      docs/en/UI/Angular/Config-State.md
  98. 78
      docs/en/UI/Angular/Environment.md
  99. 44
      docs/en/UI/Angular/List-Service.md
  100. 139
      docs/en/UI/Angular/Localization.md

18
.github/workflows/auto-pr.yml

@ -1,24 +1,24 @@
name: Merge branch rel-4.0 with rel-3.3
name: Merge branch rel-4.1 with rel-4.0
on:
push:
branches:
- rel-3.3
- rel-4.0
jobs:
merge-rel-4-0-with-rel-3-3:
merge-rel-4-1-with-rel-4-0:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: rel-4.0
ref: rel-4.1
- name: Reset promotion branch
run: |
git fetch origin rel-3.3:rel-3.3
git reset --hard rel-3.3
git fetch origin rel-4.0:rel-4.0
git reset --hard rel-4.0
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
branch: auto-merge/rel-3-3/${{github.run_number}}
title: Merge branch rel-4.0 with rel-3.3
body: This PR generated automatically to merge rel-4.0 with rel-3.3. Please review the changed files before merging to prevent any errors that may occur.
branch: auto-rel-4-0-merge-pr-${{github.run_number}}
title: Merge branch rel-4.1 with ${{github.ref}}
body: This PR generated automatically to merge rel-4.1 with rel-4.0. Please review the changed files before merging to prevent any errors that may occur.
reviewers: ${{github.actor}}
token: ${{ github.token }}

2
.github/workflows/build-and-test.yml

@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@master
with:
dotnet-version: 3.1.102
dotnet-version: 5.0.100
- name: Build All
run: .\build-all.ps1

4
.gitignore

@ -6,6 +6,7 @@
*.user
*.userosscache
*.sln.docstates
*.editorconfig
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
@ -305,3 +306,6 @@ modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Logs/
/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package-lock.json
abp-build-config.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Pages/Test/
# Identity Server temp signature file
tempkey.jwk

7
NuGet.Config

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="BlazoriseMyGet" value="https://www.myget.org/F/blazorise/api/v3/index.json" />
</packageSources>
</configuration>

99
README.md

@ -5,19 +5,102 @@
[![MyGet (with prereleases)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://docs.abp.io/en/abp/latest/Nightly-Builds)
[![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)
ABP is an **open source application framework** focused on ASP.NET Core based web application development, but also supports developing other type of applications.
ABP Framework is a complete **infrastructure** based on the **ASP.NET Core** to create **modern web applications** and **APIs** by following the software development **best practices** and the **latest technologies**.
## Links
## Getting Started
* <a href="https://abp.io/" target="_blank">Official Web Site</a>
- [Getting Started Guide](https://docs.abp.io/en/abp/4.0/Getting-Started) is the easiest way to start a new web application with the ABP Framework.
- [Web Application Development Tutorial](https://docs.abp.io/en/abp/4.0/Tutorials/Part-1) is a complete tutorial to develop a full stack web application.
### Quick Start
Install the ABP CLI:
````bash
> dotnet tool install -g Volo.Abp.Cli
````
Create a new solution:
````bash
> abp new BookStore -u mvc -d ef
````
> See the [CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all available options.
### UI Framework Options
<img width="500" src="docs/en/images/ui-options.png">
### Database Provider Options
<img width="500" src="docs/en/images/db-options.png">
## What ABP Provides?
ABP provides a **full stack developer experience**.
### Architecture
<img src="docs/en/images/ddd-microservice-simple.png">
ABP offers a complete, **modular** and **layered** software architecture based on **[Domain Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design)** principles and patterns. It also provides the necessary infrastructure and guiding to [implement this architecture](https://docs.abp.io/en/abp/4.0/Domain-Driven-Design-Implementation-Guide).
ABP Framework is suitable for **[microservice solutions](https://docs.abp.io/en/abp/latest/Microservice-Architecture)** as well as monolithic applications.
### Infrastructure
There are a lot of features provided by the ABP Framework to achieve real world scenarios easier, like [Event Bus](https://docs.abp.io/en/abp/4.0/Event-Bus), [Background Job System](https://docs.abp.io/en/abp/4.0/Background-Jobs), [Audit Logging](https://docs.abp.io/en/abp/4.0/Audit-Logging), [BLOB Storing](https://docs.abp.io/en/abp/4.0/Blob-Storing), [Data Seeding](https://docs.abp.io/en/abp/4.0/Data-Seeding), [Data Filtering](https://docs.abp.io/en/abp/4.0/Data-Filtering), etc.
### Cross Cutting Concerns
ABP also simplifies (and even automates wherever possible) cross cutting concerns and common non-functional requirements like [Exception Handling](https://docs.abp.io/en/abp/4.0/Exception-Handling), [Validation](https://docs.abp.io/en/abp/4.0/Validation), [Authorization](https://docs.abp.io/en/abp/4.0/Authorization), [Localization](https://docs.abp.io/en/abp/4.0/Localization), [Caching](https://docs.abp.io/en/abp/4.0/Caching), [Dependency Injection](https://docs.abp.io/en/abp/4.0/Dependency-Injection), [Setting Management](https://docs.abp.io/en/abp/4.0/Settings), etc.
### Application Modules
ABP is a modular framework and the Application Modules provide **pre-built application functionalities**;
- [**Account**](https://docs.abp.io/en/abp/4.0/Modules/Account): Provides UI for the account management and allows user to login/register to the application.
- **[Identity](https://docs.abp.io/en/abp/4.0/Modules/Identity)**: Manages organization units, roles, users and their permissions, based on the Microsoft Identity library.
- [**IdentityServer**](https://docs.abp.io/en/abp/4.0/Modules/IdentityServer): Integrates to IdentityServer4.
- [**Tenant Management**](https://docs.abp.io/en/abp/4.0/Modules/Tenant-Management): Manages tenants for a [multi-tenant](https://docs.abp.io/en/abp/4.0/Multi-Tenancy) (SaaS) application.
See the [Application Modules](https://docs.abp.io/en/abp/4.0/Modules/Index) document for all pre-built modules.
### Startup Templates
The [Startup templates](https://docs.abp.io/en/abp/4.0/Startup-Templates/Index) are pre-built Visual Studio solution templates. You can create your own solution based on these templates to **immediately start your development**.
## ABP Community
### ABP Community Web Site
The [ABP Community](https://community.abp.io/) is a website to publish **articles** and share **knowledge** about the ABP Framework. You can also create content for the community!
### Blog
Follow the [ABP Blog](https://blog.abp.io/) to learn the latest happenings in the ABP Framework.
### Samples
See the [sample projects](https://docs.abp.io/en/abp/4.0/Samples/Index) built with the ABP Framework.
### Want to Contribute?
ABP is a community-driven open source project. See [the contribution guide](https://docs.abp.io/en/abp/4.0/Contribution/Index) if you want to be a part of this project.
## Official Links
* <a href="https://abp.io/" target="_blank">Main Web Site</a>
* <a href="https://abp.io/get-started" target="_blank">Get Started</a>
* <a href="https://abp.io/features" target="_blank">Features</a>
* <a href="https://docs.abp.io/" target="_blank">Documentation</a>
* <a href="https://docs.abp.io/en/abp/latest/Samples/Index" target="_blank">Samples</a>
* <a href="https://blog.abp.io/" target="_blank">Blog</a>
* <a href="https://docs.abp.io/" target="_blank">Documentation</a>
* <a href="https://docs.abp.io/en/abp/latest/Samples/Index" target="_blank">Samples</a>
* <a href="https://blog.abp.io/" target="_blank">Blog</a>
* <a href="https://community.abp.io/" target="_blank">Community</a>
* <a href="https://stackoverflow.com/questions/tagged/abp" target="_blank">Stack overflow</a>
* <a href="https://twitter.com/abpframework" target="_blank">Twitter</a>
## Contribution
## Support the ABP Framework
ABP is an open source platform. Check [the contribution guide](docs/en/Contribution/Index.md) if you want to contribute to the project.
Love ABP Framework? **Please give a star** to this repository :star:

14
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/de-DE.json

@ -0,0 +1,14 @@
{
"culture": "de-DE",
"texts": {
"Account": "ABP Benutzerkonto - Anmeldung & Registrierung | ABP.IO",
"Welcome": "Willkommen",
"UseOneOfTheFollowingLinksToContinue": "Nutzen Sie einen der nachfolgenden Links um fortzusetzen",
"FrameworkHomePage": "Framework Website",
"FrameworkDocumentation": "Framework Dokumentation",
"OfficialBlog": "Offizieller Blog",
"CommercialHomePage": "Commercial Website",
"CommercialSupportWebSite": "Commercial Support-Website",
"CommunityWebSite": "ABP Community-Website"
}
}

14
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/es.json

@ -0,0 +1,14 @@
{
"culture": "es",
"texts": {
"Account": "Cuenta de ABP - Iniciar sesión y registrarse | ABP.IO",
"Welcome": "Bienvenido",
"UseOneOfTheFollowingLinksToContinue": "Usa uno de los siguientes links para continuar",
"FrameworkHomePage": "Página de inicio del framework",
"FrameworkDocumentation": "Documentación del framework",
"OfficialBlog": "Blog Oficial",
"CommercialHomePage": "Página de inicio comercial",
"CommercialSupportWebSite": "Sitio web de soporte comercial",
"CommunityWebSite": "Sitio web comunidad ABP"
}
}

199
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/de-DE.json

@ -0,0 +1,199 @@
{
"culture": "de-DE",
"texts": {
"Permission:Organizations": "Organisationen",
"Permission:Manage": "Organisationen verwalten",
"Permission:DiscountRequests": "Rabattanfragen",
"Permission:DiscountManage": "Rabattanfragen verwalten",
"Permission:Disable": "Deaktivieren",
"Permission:Enable": "Aktivieren",
"Permission:EnableSendEmail": "E-Mail-Senden aktivieren",
"Permission:SendEmail": "E-Mail senden",
"Permission:NpmPackages": "NPM-Pakete",
"Permission:NugetPackages": "Nuget-Pakete",
"Permission:Maintenance": "Wartung",
"Permission:Maintain": "Warten",
"Permission:ClearCaches": "Caches leeren",
"Permission:Modules": "Module",
"Permission:Packages": "Pakete",
"Permission:Edit": "Bearbeiten",
"Permission:Delete": "Löschen",
"Permission:Create": "Erstellen",
"Permission:Accounting": "Abrechnung",
"Permission:Accounting:Quotation": "Angebot",
"Permission:Accounting:Invoice": "Rechnung",
"Menu:Organizations": "Organisationen",
"Menu:Accounting": "Abrechnung",
"Menu:Packages": "Pakete",
"Menu:DiscountRequests": "Rabattanfragen",
"NpmPackageDeletionWarningMessage": "Dieses NPM-Paket wird entfernt. Bestätigen Sie das?",
"NugetPackageDeletionWarningMessage": "Dieses Nuget-Paket wird entfernt. Bestägiten Sie das?",
"ModuleDeletionWarningMessage": "Dieses Modul wird entfernt. Bestätigen Sie das?",
"Name": "Name",
"DisplayName": "Anzeigename",
"ShortDescription": "Kurzbeschreibung",
"NameFilter": "Name",
"CreationTime": "Erstellungszeitpunkt",
"IsPro": "Ist pro",
"ShowOnModuleList": "In Modulliste anzeigen",
"EfCoreConfigureMethodName": "Methodenname konfigurieren",
"IsProFilter": "Ist pro",
"ApplicationType": "Anwendungstyp",
"Target": "Ziel",
"TargetFilter": "Ziel",
"ModuleClass": "Modulklasse",
"NugetPackageTarget.DomainShared": "Gemeinsame Domain",
"NugetPackageTarget.Domain": "Domain",
"NugetPackageTarget.Application": "Anwendung",
"NugetPackageTarget.ApplicationContracts": "Anwedungsverträge",
"NugetPackageTarget.HttpApi": "HTTP-API",
"NugetPackageTarget.HttpApiClient": "HTTP-API-Client",
"NugetPackageTarget.Web": "Web",
"NugetPackageTarget.EntityFrameworkCore": "DeleteAllEntityFramework Core",
"NugetPackageTarget.MongoDB": "MongoDB",
"Edit": "Bearbeiten",
"Delete": "Löschen",
"Refresh": "Aktualisieren",
"NpmPackages": "NPM-Pakete",
"NugetPackages": "Nuget-Pakete",
"NpmPackageCount": "NPM-Paketanzahl",
"NugetPackageCount": "Nuget-Paketanzahl",
"Module": "Module",
"ModuleInfo": "Modulinfo",
"CreateANpmPackage": "Erstellen Sie ein NPM Paket",
"CreateAModule": "Erstellen Sie in Modul",
"CreateANugetPackage": "Erstellen Sei ein Nuget-Paket",
"AddNew": "Neu hinzufügen",
"PackageAlreadyExist{0}": "\"{0}\" Paket ist bereits hinzugefügt.",
"ModuleAlreadyExist{0}": "\"{0}\" Modul ist bereits hinzugefügt.",
"ClearCache": "Cache leeren",
"SuccessfullyCleared": "Erfolgreich geleert",
"Menu:NpmPackages": "NPM-Pakete",
"Menu:Modules": "Module",
"Menu:Maintenance": "Wartung",
"Menu:NugetPackages": "Nuget-Pakete",
"CreateAnOrganization": "Erstellen Sie eine Organisation",
"Organizations": "Organisationen",
"LongName": "Lange Name",
"LicenseType": "Lizenztyp",
"MissingLicenseTypeField": "Das Feld Lizenztyp ist erforderlich!",
"LicenseStartTime": "Startzeit der Lizenz",
"LicenseEndTime": "Endzeit der Lizenz",
"AllowedDeveloperCount": "Zulässige Entwickleranzahl",
"UserNameOrEmailAddress": "Benutzername oder E-Mail-Adresse",
"AddOwner": "Besitzer hinzufügen",
"UserName": "Benutzername",
"Email": "E-Mail",
"Developers": "Entwickler",
"AddDeveloper": "Entwickler hinzufügen",
"Create": "Erstellen",
"UserNotFound": "Benutzer nicht gefunden",
"{0}WillBeRemovedFromDevelopers": "{0} wird von den Entwicklern entfernt. Bestätigen Sie das?",
"{0}WillBeRemovedFromOwners": "{0} wird von den Besitzern entfernt. Bestätigen Sie das?",
"Computers": "Computer",
"UniqueComputerId": "Eindeutig Computer-ID",
"LastSeenDate": "Zuletzt gesehenes Datum",
"{0}Computer{1}WillBeRemovedFromRecords": "Computer von {0} ({1}) wird aus den Datensätzen entfernt",
"OrganizationDeletionWarningMessage": "Organisation wird gelöscht",
"DeletingLastOwnerWarningMessage": "Eine Organisation muss zumindest einen Besitzer aufweisen! Daher können Sie diesen Besitzer nicht entfernen",
"This{0}AlreadyExistInThisOrganization": "Dies {0} existiert bereits in dieser Organisation",
"AreYouSureYouWantToDeleteAllComputers": "Sind Sie sicher, dass Sie alle Computer löschen möchten?",
"DeleteAll": "Alles Löschen",
"DoYouWantToCreateNewUser": "Möchten Sie einen neuen Benutzer erstellen?",
"MasterModules": "Master-Module",
"OrganizationName": "Organisationsname",
"CreationDate": "Erstellungsdatum",
"LicenseStartDate": "Startdatum der Lizenz",
"LicenseEndDate": "Enddatum der Lizenz",
"OrganizationNamePlaceholder": "Organisationsname...",
"TotalQuestionCountPlaceholder": "Gesamtzahl der Fragen...",
"RemainingQuestionCountPlaceholder": "Anzahl verbleibender Fragen...",
"LicenseTypePlaceholder": "Lizenztyp...",
"CreationDatePlaceholder": "Erstellungsdatum...",
"LicenseStartDatePlaceholder": "Startdatum der Lizenz...",
"LicenseEndDatePlaceholder": "Enddatum der Lizenz...",
"UsernameOrEmail": "Benutzername oder E-Mail-Adresse",
"UsernameOrEmailPlaceholder": "Benutzername oder E-Mail-Adresse...",
"Member": "Mitglied",
"PurchaseOrderNo": "Bestellnummer",
"QuotationDate": "Angebotsdatum",
"CompanyName": "Firmenname",
"CompanyAddress": "Firmenanschrift",
"Price": "Preis",
"DiscountText": "Rabatttext",
"DiscountQuantity": "Rabattmenge",
"DiscountPrice": "Rabattpreis",
"Quotation": "Angebot",
"ExtraText": "Zusätzlicher Text",
"ExtraAmount": "Zusätzliche Menge",
"DownloadQuotation": "Angebot herunterladen",
"Invoice": "Rechnung",
"TaxNumber": "Steuernummer",
"InvoiceNumber": "Rechnungsnummer",
"InvoiceDate": "Rechnungsdatum",
"InvoiceNote": "Rechnungsnotiz",
"Quantity": "Menge",
"AddProduct": "Produkt hinzufügen",
"AddProductWarning": "Sie müssen ein Produkt hinzufügen!",
"TotalPrice": "Gesamtpreis",
"Generate": "Generieren",
"MissingQuantityField": "Das Feld Menge ist erforderlich!",
"MissingPriceField": "Das Feld Preis ist erforderlich!",
"CodeUsageStatus": "Status",
"Country": "Land",
"DeveloperCount": "Entwickleranzahl",
"RequestCode": "Anfrage-Code",
"WebSite": "Webseite",
"GithubUsername": "Github Benutzername",
"PhoneNumber": "Telefonnummer",
"ProjectDescription": "Projektbeschreibung",
"Referrer": "Referrer",
"DiscountRequests": "Rabattanfrage",
"Copylink": "Link kopieren",
"Disable": "Deaktivieren",
"Enable": "Aktivieren",
"EnableSendEmail": "E-Mail-Senden aktivieren",
"SendEmail": "E-Mail senden",
"SuccessfullyDisabled": "Erfolgreich deaktiviert",
"SuccessfullyEnabled": "Erfolgreich aktiviert",
"EmailSent": "E-Mail gesendet",
"SuccessfullySent": "Erfolgreich gesendet",
"SuccessfullyDeleted": "Erfolgreich gelöscht",
"DiscountRequestDeletionWarningMessage": "Rabattanfrage wird gelöscht",
"BusinessType": "Unternehmensart",
"TotalQuestionCount": "Gesamtzahl der Fragen",
"RemainingQuestionCount": "Anzahl verbleibender Fragen",
"TotalQuestionMustBeGreaterWarningMessage": "TotalQuestionCount muss größer sein als RemainingQuestionCount !",
"QuestionCountsMustBeGreaterThanZero": "TotalQuestionCount und RemainingQuestionCount müssen Null oder größer als Null sein !",
"UnlimitedQuestionCount": "Unbegrenzte Anzahl von Fragen",
"Notes": "Anmerkungen",
"Menu:Community": "Community",
"Menu:Articles": "Beiträge",
"Wait": "Warten",
"Approve": "Genehmigen",
"Reject": "Ablehnen",
"Details": "Details",
"Url": "URL",
"Title": "Titel",
"ContentSource": "Inhaltsquelle",
"Status": "Status",
"ReadArticle": "Beitrag lesen",
"ArticleHasBeenWaiting": "Beitrag hat gewartet",
"ArticleHasBeenApproved": "Beitrag wurde genehmigt",
"ArticleHasBeenRejected": "Beitrag wurde abgelehnt",
"Permission:Community": "Community",
"Permission:CommunityArticle": "Beitrag",
"Link": "Link",
"Enum:ContentSource:0": "Github",
"Enum:ContentSource:1": "Extern",
"Enum:Status:0": "Wartend",
"Enum:Status:1": "Abgelehnt",
"Enum:Status:2": "Genehmigt",
"Summary": "Zusammenfassung",
"AuthorName": "Autorenname",
"CoverImage": "Titelbild",
"RemoveCacheConfirmationMessage": "Sind Sie sicher, dass Sie den Cache für den Artikel \"{0}\" entfernen wollen?",
"SuccessfullyRemoved": "Erfolgreich geleert",
"RemoveCache": "Cache entfernen"
}
}

199
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/es.json

@ -0,0 +1,199 @@
{
"culture": "es",
"texts": {
"Permission:Organizations": "Organizaciones",
"Permission:Manage": "Gestionar organizaciones",
"Permission:DiscountRequests": "Solicitudes de descuento",
"Permission:DiscountManage": "Gestionar solicitudes de descuento",
"Permission:Disable": "Desactivar",
"Permission:Enable": "Activar",
"Permission:EnableSendEmail": "Activar enviar email",
"Permission:SendEmail": "Enviar email",
"Permission:NpmPackages": "Paquetes NPM",
"Permission:NugetPackages": "Paquetes Nuget",
"Permission:Maintenance": "Mantenimiento",
"Permission:Maintain": "Mantener",
"Permission:ClearCaches": "Borrar cachés",
"Permission:Modules": "Módulos",
"Permission:Packages": "Paquetes",
"Permission:Edit": "Editar",
"Permission:Delete": "Borrar",
"Permission:Create": "Crear",
"Permission:Accounting": "Contabilidad",
"Permission:Accounting:Quotation": "Cotización",
"Permission:Accounting:Invoice": "Factura",
"Menu:Organizations": "Organizaciones",
"Menu:Accounting": "Contabilidad",
"Menu:Packages": "Paquetes",
"Menu:DiscountRequests": "Solicitudes de descuento",
"NpmPackageDeletionWarningMessage": "Este NPM paquete será borrado. ¿Quieres confirmar?",
"NugetPackageDeletionWarningMessage": "Este NPM paquete será borrado. ¿Quieres confirmar?",
"ModuleDeletionWarningMessage": "Este NPM paquete será borrado. ¿Quieres confirmar?",
"Name": "Nombre",
"DisplayName": "Nombre para mostrar",
"ShortDescription": "Descripción corta",
"NameFilter": "Nombre",
"CreationTime": "Fecha de creación",
"IsPro": "Es pro",
"ShowOnModuleList": "Mostrar en la lista de módulos",
"EfCoreConfigureMethodName": "configurar nombre de método",
"IsProFilter": "Es pro",
"ApplicationType": "tipo de aplicación",
"Target": "Destino",
"TargetFilter": "Destino",
"ModuleClass": "Módulo de clase",
"NugetPackageTarget.DomainShared": "Dominio compartido",
"NugetPackageTarget.Domain": "Dominio",
"NugetPackageTarget.Application": "Aplicación",
"NugetPackageTarget.ApplicationContracts": "Contratos de aplicación",
"NugetPackageTarget.HttpApi": "Http Api",
"NugetPackageTarget.HttpApiClient": "Cliente Http Api",
"NugetPackageTarget.Web": "Web",
"NugetPackageTarget.EntityFrameworkCore": "Delete todo EntityFramework Core",
"NugetPackageTarget.MongoDB": "MongoDB",
"Edit": "Editar",
"Delete": "Borrar",
"Refresh": "Refrescar",
"NpmPackages": "Paquetes NPM",
"NugetPackages": "Paquetes Nuget",
"NpmPackageCount": "Número de paquetes NPM",
"NugetPackageCount": "Número de paquetes Nuget",
"Module": "Módulos",
"ModuleInfo": "Info de módulo",
"CreateANpmPackage": "Crear un paquete NPM",
"CreateAModule": "Crear un módulo",
"CreateANugetPackage": "Crear un paquete de Nuget",
"AddNew": "Añadir nuevo",
"PackageAlreadyExist{0}": "\"{0}\" paquete ya se encuentra añadido",
"ModuleAlreadyExist{0}": "\"{0}\" módulo ya se encuentra añadido.",
"ClearCache": "Borrar caché",
"SuccessfullyCleared": "Borrado satisfactoriamente",
"Menu:NpmPackages": "Paquetes de NPM",
"Menu:Modules": "Módulos",
"Menu:Maintenance": "Mantenimiento",
"Menu:NugetPackages": "Paquetes Nuget",
"CreateAnOrganization": "Crear una organización",
"Organizations": "Organizaciones",
"LongName": "Nombre largo",
"LicenseType": "Tipo de licencia",
"MissingLicenseTypeField": "El campo tipo de licencia es requerido!",
"LicenseStartTime": "Fecha de inicio de licencia",
"LicenseEndTime": "Fecha de caducidad de licencia",
"AllowedDeveloperCount": "Número de desarrolladores permitidos",
"UserNameOrEmailAddress": "Nombre de usuario o",
"AddOwner": "Añadir propietario",
"UserName": "Nombre de usuario",
"Email": "Email",
"Developers": "Desarrolladores",
"AddDeveloper": "Añadir desarrollador",
"Create": "Crear",
"UserNotFound": "Usuario no encontrado",
"{0}WillBeRemovedFromDevelopers": "{0} será eliminado de desarrolladores, ¿deseas continuar?",
"{0}WillBeRemovedFromOwners": "{0} será eliminado de propietarios, ¿deseas continuar?",
"Computers": "Ordenadores",
"UniqueComputerId": "Id única de ordenador",
"LastSeenDate": "Fecha de visto por última vez",
"{0}Computer{1}WillBeRemovedFromRecords": "El ordenador {0} ({1}) será eliminado de los registros",
"OrganizationDeletionWarningMessage": "La organización será eliminada",
"DeletingLastOwnerWarningMessage": "Una organización debe tener al menos un propietario!. Por lo tanto, tu no puedes eliminar este propietario",
"This{0}AlreadyExistInThisOrganization": "Este/a {0} ya existe en esta organización",
"AreYouSureYouWantToDeleteAllComputers": "Estás seguro tu quieres eliminar todos los ordenadores",
"DeleteAll": "Eliminar todo",
"DoYouWantToCreateNewUser": "¿Quieres crear un nuevo usuario?",
"MasterModules": "Módulos maestros",
"OrganizationName": "Nombre de organización",
"CreationDate": "Fecha de creación",
"LicenseStartDate": "Fecha de inicio de licencia",
"LicenseEndDate": "Fecha de caducidad de licencia",
"OrganizationNamePlaceholder": "Nombre de organización...",
"TotalQuestionCountPlaceholder": "Número total de preguntas...",
"RemainingQuestionCountPlaceholder": "Número de preguntas pendientes",
"LicenseTypePlaceholder": "Tipo de licencia...",
"CreationDatePlaceholder": "Fecha de creación...",
"LicenseStartDatePlaceholder": "Fecha de inicio de licencia...",
"LicenseEndDatePlaceholder": "Fecha de caducidad de licencia...",
"UsernameOrEmail": "Usuario o email",
"UsernameOrEmailPlaceholder": "Usuario o email...",
"Member": "Miembro",
"PurchaseOrderNo": "Número de orden de compra",
"QuotationDate": "",
"CompanyName": "",
"CompanyAddress": "",
"Price": "",
"DiscountText": "",
"DiscountQuantity": "",
"DiscountPrice": "",
"Quotation": "",
"ExtraText": "",
"ExtraAmount": "",
"DownloadQuotation": "",
"Invoice": "",
"TaxNumber": "",
"InvoiceNumber": "",
"InvoiceDate": "",
"InvoiceNote": "",
"Quantity": "",
"AddProduct": "",
"AddProductWarning": "",
"TotalPrice": "",
"Generate": "",
"MissingQuantityField": "",
"MissingPriceField": "",
"CodeUsageStatus": "",
"Country": "",
"DeveloperCount": "",
"RequestCode": "",
"WebSite": "",
"GithubUsername": "",
"PhoneNumber": "",
"ProjectDescription": "",
"Referrer": "",
"DiscountRequests": "",
"Copylink": "",
"Disable": "",
"Enable": "",
"EnableSendEmail": "",
"SendEmail": "",
"SuccessfullyDisabled": "",
"SuccessfullyEnabled": "",
"EmailSent": "",
"SuccessfullySent": "",
"SuccessfullyDeleted": "",
"DiscountRequestDeletionWarningMessage": "",
"BusinessType": "",
"TotalQuestionCount": "",
"RemainingQuestionCount": "",
"TotalQuestionMustBeGreaterWarningMessage": "",
"QuestionCountsMustBeGreaterThanZero": "",
"UnlimitedQuestionCount": "",
"Notes": "",
"Menu:Community": "",
"Menu:Articles": "",
"Wait": "",
"Approve": "",
"Reject": "",
"Details": "",
"Url": "",
"Title": "",
"ContentSource": "",
"Status": "",
"ReadArticle": "",
"ArticleHasBeenWaiting": "",
"ArticleHasBeenApproved": "",
"ArticleHasBeenRejected": "",
"Permission:Community": "",
"Permission:CommunityArticle": "",
"Link": "",
"Enum:ContentSource:0": "",
"Enum:ContentSource:1": "",
"Enum:Status:0": "",
"Enum:Status:1": "",
"Enum:Status:2": "",
"Summary": "",
"AuthorName": "",
"CoverImage": "",
"RemoveCacheConfirmationMessage": "",
"SuccessfullyRemoved": "",
"RemoveCache": ""
}
}

33
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/de-DE.json

@ -0,0 +1,33 @@
{
"culture": "de-DE",
"texts": {
"Volo.AbpIo.Domain:010004": "Maximale Mitgliederanzahl erreicht!",
"Volo.AbpIo.Domain:010005": "Miximale Besizeranzahl erreicht!",
"Volo.AbpIo.Domain:010006": "Dieser Benutzer ist bereits ein Besitzer in dieser Organisation!",
"Volo.AbpIo.Domain:010007": "Dieser Benutzer ist bereits ein Entwickler in dieser Organisation!",
"Volo.AbpIo.Domain:010008": "Die zulässige Entwickleranzahl darf nicht geringer sein als die aktuelle Entwickleranzahl!",
"Volo.AbpIo.Domain:010009": "Die zulässige Entwickleranzahl darf nicht kleiner als 0 sein!",
"Volo.AbpIo.Domain:010010": "Die maximale Anzahl der Mac-Adressen ist überschritten!",
"Volo.AbpIo.Domain:010011": "Die persönliche Lizenz kann nicht mehr als 1 Entwickler haben!",
"Volo.AbpIo.Domain:010012": "Die Lizenz kann nicht einen Monat nach Ablauf der Lizenz verlängert werden!",
"Volo.AbpIo.Domain:020001": "Dieses NPM-Paket konnte nicht gelöscht werden, da \"{NugetPackages}\" Nuget-Pakete von diesem Paket abhängig sind.",
"Volo.AbpIo.Domain:020002": "Dieses NPM-Paket konnte nicht gelöscht werden, da \"{Module}\" Module dieses Paket verwenden.",
"Volo.AbpIo.Domain:020003": "Dieses NPM-Paket konnte nicht gelöscht werden, da \"{Module}\" Module dieses Paket verwenden und \"{NugetPackages}\" Nuget-Pakete von diesem Paket abhängig sind.",
"Volo.AbpIo.Domain:020004": "Dieses Nuget-Paket konnte nicht gelöscht werden, da \"{Module}\" Module dieses Paket verwenden.",
"WantToLearn?": "Wollen Sie sich einlernen?",
"ReadyToGetStarted?": "Bereit anzufangen?",
"JoinOurCommunity": "Tritt unserer Community bei",
"GetStartedUpper": "LOSLEGEN",
"ForkMeOnGitHub": "Fork me on GitHub",
"Features": "Features",
"GetStarted": "Loslegen",
"Documents": "Unterlagen",
"Community": "Community",
"ContributionGuide": "Leitfaden für Mitwirkende",
"Blog": "Blog",
"Commercial": "Commercial",
"MyAccount": "Mein Benutzerkonto",
"SeeDocuments": "Siehe Unterlagen",
"Samples": "Beispiele"
}
}

33
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/es.json

@ -0,0 +1,33 @@
{
"culture": "es",
"texts": {
"Volo.AbpIo.Domain:010004": "",
"Volo.AbpIo.Domain:010005": "",
"Volo.AbpIo.Domain:010006": "",
"Volo.AbpIo.Domain:010007": "",
"Volo.AbpIo.Domain:010008": "",
"Volo.AbpIo.Domain:010009": "",
"Volo.AbpIo.Domain:010010": "",
"Volo.AbpIo.Domain:010011": "",
"Volo.AbpIo.Domain:010012": "",
"Volo.AbpIo.Domain:020001": "",
"Volo.AbpIo.Domain:020002": "",
"Volo.AbpIo.Domain:020003": "",
"Volo.AbpIo.Domain:020004": "",
"WantToLearn?": "",
"ReadyToGetStarted?": "",
"JoinOurCommunity": "",
"GetStartedUpper": "",
"ForkMeOnGitHub": "",
"Features": "",
"GetStarted": "",
"Documents": "",
"Community": "",
"ContributionGuide": "",
"Blog": "",
"Commercial": "",
"MyAccount": "",
"SeeDocuments": "",
"Samples": ""
}
}

35
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de-DE.json

@ -0,0 +1,35 @@
{
"culture": "de-DE",
"texts": {
"OrganizationManagement": "Organisationsverwaltung",
"OrganizationList": "Organisationsauflistung",
"Volo.AbpIo.Commercial:010003": "Sie sind der Besitzer dieser Organisation!",
"OrganizationNotFoundMessage": "Keine Organisation gefunden!",
"DeveloperCount": "Zugeordnete / Gesamte Entwickler",
"QuestionCount": "Verbleibende / Gesamte Fragen",
"Unlimited": "Unbegrenzt",
"Owners": "Besitzer",
"AddMember": "Mitglied hinzufügen",
"AddOwner": "Besizer hinzufügen",
"AddDeveloper": "Entwickler hinzufügen",
"UserName": "Benutzername",
"Name": "Name",
"EmailAddress": "E-Mail-Adress",
"Developers": "Entwickler",
"LicenseType": "Lizenztyp",
"Manage": "Verwalten",
"StartDate": "Startdatum",
"EndDate": "Enddatum",
"Modules": "Module",
"LicenseExtendMessage": "Ihr Lizenzenddatum wird auf {0} verlängert",
"LicenseUpgradeMessage": "Ihre Lizenz wird auf {0} aktualisiert",
"LicenseAddDeveloperMessage": "{0} Entwickler zu Ihrer Lizenz hinzugefügt",
"Volo.AbpIo.Commercial:010004": "Kann den angegebenen Benutzer nicht finden! Der Benutzer muss sich bereits registriert haben.",
"MyOrganizations": "Meine Organisationen",
"ApiKey": "API-Schlüssel",
"UserNameNotFound": "Es gibt keinen Benutzer mit dem Benutzernamen {0}",
"SuccessfullyAddedToNewsletter": "Vielen Dank, dass Sie unseren Newsletter abonniert haben!",
"MyProfile": "Mein Profil",
"EmailNotValid": "Bitte geben Sie eine gültige E-Mail-Adresse ein."
}
}

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

@ -30,6 +30,8 @@
"UserNameNotFound": "There is no user with username {0}",
"SuccessfullyAddedToNewsletter": "Thanks you for subscribing to our newsletter!",
"MyProfile": "My profile",
"EmailNotValid": "Please enter a valid email address."
"EmailNotValid": "Please enter a valid email address.",
"JoinOurMarketingNewsletter": "Join our marketing newsletter",
"WouldLikeToReceiveMarketingMaterials": "I would like to receive marketing materials like product deals & special offers."
}
}

35
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json

@ -0,0 +1,35 @@
{
"culture": "es",
"texts": {
"OrganizationManagement": "",
"OrganizationList": "",
"Volo.AbpIo.Commercial:010003": "",
"OrganizationNotFoundMessage": "",
"DeveloperCount": "",
"QuestionCount": "",
"Unlimited": "",
"Owners": "",
"AddMember": "",
"AddOwner": "",
"AddDeveloper": "",
"UserName": "",
"Name": "",
"EmailAddress": "",
"Developers": "",
"LicenseType": "Tipo de licencia",
"Manage": "",
"StartDate": "",
"EndDate": "",
"Modules": "Módulos",
"LicenseExtendMessage": "",
"LicenseUpgradeMessage": "",
"LicenseAddDeveloperMessage": "",
"Volo.AbpIo.Commercial:010004": "",
"MyOrganizations": "",
"ApiKey": "",
"UserNameNotFound": "",
"SuccessfullyAddedToNewsletter": "",
"MyProfile": "",
"EmailNotValid": ""
}
}

90
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de-DE.json

@ -0,0 +1,90 @@
{
"culture": "de-DE",
"texts": {
"Permission:CommunityArticle": "Community-Beitrag",
"Permission:Edit": "Bearbeiten",
"Waiting": "Wartend",
"Approved": "Genehmigt",
"Rejected": "Abgelehnt",
"Wait": "Warten",
"Approve": "Genehmigen",
"Reject": "Ablehnen",
"ReadArticle": "Beitrag lesen",
"Status": "Status",
"ContentSource": "Inhaltsquelle",
"Details": "Details",
"Url": "URL",
"Title": "Titel",
"CreationTime": "Erstellungszeitpunkt",
"Save": "Speichern",
"SameUrlAlreadyExist": "Dieselbe URL existiert bereits, wenn Sie diesen Beitrag hinzufügen möchten, sollten Sie die URL ändern!",
"UrlIsNotValid": "Der URL ist nicht korrekt.",
"UrlNotFound": "URL nicht gefunden.",
"UrlContentNotFound": "URL-Inhalt nicht gefunden",
"Summary": "Zusammenfassung",
"MostRead": "Meist gelesen",
"Latest": "Neueste",
"ContributeAbpCommunity": "Tragen Sie zur ABP Community bei",
"SubmitYourArticle": "Reichen Sie Ihren Beitrag ein",
"ContributionGuide": "Leitfaden für Mitwirkende",
"BugReport": "Fehler melden",
"SeeAllArticles": "Alle Beiträge anzeigen",
"WelcomeToABPCommunity!": "Willkommen in der ABP Community!",
"MyProfile": "Mein Profil",
"MyOrganizations": "Meine Organisationen",
"EmailNotValid": "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
"FeatureRequest": "Featureanfrage",
"CreateArticleTitleInfo": "Titel des Beitrags, der in der Beitragsliste angezeigt werden soll.",
"CreateArticleUrlInfo": "Original GitHub-/externe URL des Beitrags.",
"CreateArticleSummaryInfo": "Eine kurze Zusammenfassung des Beitrags, der in der Beitragsliste angezeigt werden soll.",
"CreateArticleCoverInfo": "Fügen Sie zum Erstellen eines effektiven Beitrags ein Titelbild hinzu. Laden Sie Bilder mit einem Seitenverhältnis von 16: 9 hoch, um die beste Ansicht zu erhalten.",
"ThisExtensionIsNotAllowed": "Diese Erweiterung ist nicht zulässig.",
"TheFileIsTooLarge": "Die Datei ist zu groß.",
"GoToTheArticle": "Gehe zum Beitrag",
"Contribute": "Beitragen",
"OverallProgress": "Gesamtfortschritt",
"Done": "Ferig",
"Open": "Offen",
"Closed": "Geschlossen",
"LatestQuestionOnThe": "Letzte Frage zum",
"Stackoverflow": "Stackoverflow",
"Votes": "Stimmen",
"Answer": "Antworten",
"Views": "Ansichten",
"Answered": "Beantwortet",
"WaitingForYourAnswer": "Warten auf Ihre Antwort",
"Asked": "gefragt",
"AllQuestions": "Alle Fragen",
"NextVersion": "Nächste Version",
"MilestoneErrorMessage": "Die aktuellen Meilensteindetails konnten von Github nicht abgerufen werden.",
"QuestionItemErrorMessage": "Die neuesten Fragendetails konnten von Stackoverflow nicht abgerufen werden.",
"Oops": "Hoppla!",
"CreateArticleSuccessMessage": "Der Beitrag wurde erfolgreich eingereicht. Er wird nach einer Überprüfung durch den Site-Administrator veröffentlicht.",
"ChooseCoverImage": "Wählen Sie ein Titelbild ...",
"CoverImage": "Titelbild",
"ShareYourExperiencesWithTheABPFramework": "Teilen Sie Ihre Erfahrungen mit dem ABP Framework!",
"Optional": "Optional",
"UpdateUserWebSiteInfo": "Beispiel: https://johndoe.com",
"UpdateUserTwitterInfo": "Beispiel: johndoe",
"UpdateUserGithubInfo": "Beispiel: johndoe",
"UpdateUserLinkedinInfo": "Beispiel: https://www.linkedin.com/...",
"UpdateUserCompanyInfo": "Beispiel: Volosoft",
"UpdateUserJobTitleInfo": "Beispiel: Software Developer",
"UserName": "Benutzername",
"Company": "Firma",
"PersonalWebsite": "Persönliche Website",
"RegistrationDate": "Registrierungsdatum",
"Social": "Social",
"Biography": "Biographie",
"HasNoPublishedArticlesYet": "hat noch keine Beiträge veröffentlicht",
"Author": "Autor",
"LatestGithubAnnouncements": "Neueste Github-Ankündigungen",
"SeeAllAnnouncements": "Alle Ankündigungen anzeigen",
"LatestBlogPost": "Letzter Blog-Beitrag",
"Edit": "Bearbeiten",
"ProfileImageChange": "Ändern Sie das Profilbild",
"BlogItemErrorMessage": "Die neuesten Blogpost-Details konnten von ABP nicht abgerufen werden.",
"PlannedReleaseDate": "Geplantes Erscheinungsdatum",
"CommunityArticleRequestErrorMessage": "Die Anfrage nach den neuesten Beiträgen von Github konnte nicht abgerufen werden."
}
}

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

@ -84,6 +84,21 @@
"Edit": "Edit",
"ProfileImageChange": "Change the profile image",
"BlogItemErrorMessage": "Could not get the latest blog post details from ABP.",
"PlannedReleaseDate": "Planned release date"
"PlannedReleaseDate": "Planned release date",
"CommunityArticleRequestErrorMessage": "Could not get the latest article request from Github.",
"ArticleRequestFromGithubIssue": "There are not any article requests now.",
"LatestArticles": "Latest Articles",
"ArticleRequests": "Article Requests",
"AllArticleRequests": "See All Article Requests",
"SubscribeToTheNewsletter": "Subscribe to the Newsletter",
"NewsletterEmailDefinition": "Get information about happenings in ABP like new releases, free sources, articles, and more.",
"NoThanks": "No, thanks",
"MaybeLater": "Maybe later",
"JoinOurArticleNewsletter": "Join our article newsletter",
"Community": "Community",
"Marketing": "Marketing",
"CommunityPrivacyPolicyConfirmation": "I agree to the Terms & Conditions and <a href=\"https://commercial.abp.io/Privacy\">Privacy Policy</a>.",
"ArticleRequestMessageTitle": "<a href=\"https://github.com/abpframework/abp/issues/new\">Open an issue</a> on the GitHub to request an article/tutorial you want to see on this web site.",
"ArticleRequestMessageBody": "Here, the list of the requested articles by the community. Do you want to write a requested article? Please click to the request and join to the discussion."
}
}

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

@ -0,0 +1,90 @@
{
"culture": "es",
"texts": {
"Permission:CommunityArticle": "",
"Permission:Edit": "",
"Waiting": "",
"Approved": "",
"Rejected": "",
"Wait": "",
"Approve": "",
"Reject": "",
"ReadArticle": "",
"Status": "",
"ContentSource": "",
"Details": "",
"Url": "",
"Title": "",
"CreationTime": "",
"Save": "Guardar",
"SameUrlAlreadyExist": "",
"UrlIsNotValid": "",
"UrlNotFound": "",
"UrlContentNotFound": "",
"Summary": "",
"MostRead": "",
"Latest": "",
"ContributeAbpCommunity": "",
"SubmitYourArticle": "",
"ContributionGuide": "",
"BugReport": "",
"SeeAllArticles": "",
"WelcomeToABPCommunity!": "",
"MyProfile": "",
"MyOrganizations": "",
"EmailNotValid": "",
"FeatureRequest": "",
"CreateArticleTitleInfo": "",
"CreateArticleUrlInfo": "",
"CreateArticleSummaryInfo": "",
"CreateArticleCoverInfo": "",
"ThisExtensionIsNotAllowed": "",
"TheFileIsTooLarge": "",
"GoToTheArticle": "",
"Contribute": "",
"OverallProgress": "",
"Done": "",
"Open": "",
"Closed": "",
"LatestQuestionOnThe": "",
"Stackoverflow": "",
"Votes": "",
"Answer": "",
"Views": "",
"Answered": "",
"WaitingForYourAnswer": "",
"Asked": "",
"AllQuestions": "",
"NextVersion": "",
"MilestoneErrorMessage": "",
"QuestionItemErrorMessage": "",
"Oops": "",
"CreateArticleSuccessMessage": "",
"ChooseCoverImage": "",
"CoverImage": "",
"ShareYourExperiencesWithTheABPFramework": "",
"Optional": "",
"UpdateUserWebSiteInfo": "",
"UpdateUserTwitterInfo": "",
"UpdateUserGithubInfo": "",
"UpdateUserLinkedinInfo": "",
"UpdateUserCompanyInfo": "",
"UpdateUserJobTitleInfo": "",
"UserName": "",
"Company": "",
"PersonalWebsite": "",
"RegistrationDate": "",
"Social": "",
"Biography": "",
"HasNoPublishedArticlesYet": "",
"Author": "",
"LatestGithubAnnouncements": "",
"SeeAllAnnouncements": "",
"LatestBlogPost": "",
"Edit": "",
"ProfileImageChange": "",
"BlogItemErrorMessage": "",
"PlannedReleaseDate": "",
"CommunityArticleRequestErrorMessage": ""
}
}

189
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/de-DE.json

@ -0,0 +1,189 @@
{
"culture": "de-DE",
"texts": {
"GetStarted": "Erste Schritte - Startvorlagen",
"Create": "Erstellen",
"NewProject": "Neues Projekt",
"DirectDownload": "Direkter Download",
"ProjectName": "Proejktname",
"ProjectType": "Projekttyp",
"DatabaseProvider": "Datenbankanbieter",
"NTier": "N-Tier",
"IncludeUserInterface": "Benutzeroberfläche einschließen",
"CreateNow": "Jetzt erstellen",
"TheStartupProject": "Das Startprojekt",
"Tutorial": "Lernprogramm",
"UsingCLI": "mit CLI",
"SeeDetails": "Details ansehen",
"AbpShortDescription": "ABP Framework ist eine vollständige Infrastruktur zum Erstellen moderner Webanwendungen unter Befolgung von Best Practices und Konventionen für Softwareentwicklung.",
"SourceCodeUpper": "QUELLCODE",
"LatestReleaseLogs": "Akteulle Release",
"Infrastructure": "Infrastrutkur",
"Architecture": "Architektur",
"Modular": "Modular",
"DontRepeatYourself": "Don't Repeat Yourself",
"DeveloperFocused": "Entwickler-Zentriert",
"FullStackApplicationInfrastructure": "Full-Stack-Anwendungsinfrastruktur.",
"DomainDrivenDesign": "Domain Driven Design",
"DomainDrivenDesignExplanation": "Entworfen und entwickelt basierend auf DDD-Mustern und -Prinzipien. Bietet ein Schichtenmodell für Ihre Anwendung.",
"Authorization": "Authorization",
"AuthorizationExplanation": "Erweiterte Autorisierung mit Benutzer-, Rollen- und fein abgestimmtem Berechtigungssystem. Aufbauend auf der Microsoft Identity-Bibliothek.",
"MultiTenancy": "Multi-Tenancy",
"MultiTenancyExplanationShort": "SaaS-Anwendungen leicht gemacht! Integrierte Mandantenfähigkeit von der Datenbank bis zur Benutzeroberfläche.",
"CrossCuttingConcerns": "Cross Cutting Concerns",
"CrossCuttingConcernsExplanationShort": "Komplette Infrastruktur für Autorisierung, Validierung, Ausnahmebehandlung, Caching, Überwachungsprotokollierung, Transaktionsverwaltung und mehr.",
"BuiltInBundlingMinification": "Built-In Bundling & Minification",
"BuiltInBundlingMinificationExplanation": "Für die Bundling und Minification müssen keine externen Tools verwendet werden. ABP bietet eine einfachere, dynamische, leistungsstarke, modulare und integrierte Methode!",
"VirtualFileSystem": "Virtual File System",
"VirtualFileSystemExplanation": "Betten Sie Ansichten, Skripte, Stile, Bilder ... in Pakete/Bibliotheken ein und verwenden Sie sie in verschiedenen Anwendungen wieder.",
"Theming": "Theming",
"ThemingExplanationShort": "Verwenden und passen Sie das Bootstrap-basierte Standard-UI-Design an oder erstellen Sie Ihr eigenes.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms",
"BootstrapTagHelpersDynamicFormsExplanation": "Anstatt die sich wiederholenden Details von Bootstrap-Komponenten manuell zu schreiben, verwenden Sie die Tag-Helper von ABP, um diese zu vereinfachen und dabei die Vorteile von Intellisense zu nutzen. Erstellen Sie mit dem dynamischen Formular-Tag-Helfer schnell UI-Formulare basierend auf einem C#-Modell.",
"HTTPAPIsDynamicProxies": "HTTP APIs & Dynamic Proxies",
"HTTPAPIsDynamicProxiesExplanation": "Stellen Sie Anwendungsdienste automatisch als HTTP-APIs im REST-Stil bereit und verwenden Sie diese mit dynamischen JavaScript- und C#-Proxys.",
"CompleteArchitectureInfo": "Moderne Architektur zur Erstellung wartbarer Softwarelösungen.",
"DomainDrivenDesignBasedLayeringModelExplanation": "Hilft Ihnen bei der Implementierung einer DDD-basierten Schichtarchitektur und beim Aufbau einer wartbaren Codebasis.",
"DomainDrivenDesignBasedLayeringModelExplanationCont": "Bietet Startvorlagen, Abstraktionen, Basisklassen, Dienste, Dokumentation und Anleitungen, mit denen Sie Ihre Anwendung basierend auf DDD-Mustern und -Prinzipien entwickeln können.",
"MicroserviceCompatibleModelExplanation": "Das Kernframework und die vorgefertigten Module sind unter Berücksichtigung der Microservice-Architektur konzipiert.",
"MicroserviceCompatibleModelExplanationCont": "Bietet Infrastruktur, Integrationen, Beispiele und Dokumentation zur einfacheren Implementierung von Microservice-Lösungen, ohne zusätzliche Komplexität zu verursachen, wenn Sie eine monolithische Anwendung wünschen.",
"ModularInfo": "ABP bietet ein Modulsystem, mit dem Sie wiederverwendbare Anwendungsmodule entwickeln, Ereignisse im Anwendungslebenszyklus verknüpfen und Abhängigkeiten zwischen Kernteilen Ihres Systems ausdrücken können.",
"PreBuiltModulesThemes": "Vorgefertigte Module & Themes",
"PreBuiltModulesThemesExplanation": "Open Source- und kommerzielle Module und Themes stehen bereit, um in Ihrer Geschäftsanwendung verwendet zu werden.",
"NuGetNPMPackages": "NuGet- & NPM-Pakete",
"NuGetNPMPackagesExplanation": "Bereitgestellt als NuGet- & NPM-Pakete. Einfach zu installieren und zu aktualisieren.",
"ExtensibleReplaceable": "Erweiterbar/Austauschbar",
"ExtensibleReplaceableExplanation": "Alle Dienste und Module sind auf Erweiterbarkeit ausgelegt. Sie können Dienste, Seiten, Stile und Komponenten ersetzen.",
"CrossCuttingConcernsExplanation2": "Halten Sie Ihre Codebasis kleiner, damit Sie sich auf ihre geschäftsspezifischen Code konzentrieren können.",
"CrossCuttingConcernsExplanation3": "Verbringen Sie keine Zeit damit, grundlegende Anwendungsanforderungen für jedes neue Projekte zu implementieren.",
"AuthenticationAuthorization": "Authentifizierung & Autorisierung",
"ExceptionHandling": "Fehlerbehandlung",
"Validation": "Validierung",
"DatabaseConnection": "Datenbankverbindung",
"TransactionManagement": "Transaktionsmanagement",
"AuditLogging": "Audit Logging",
"Caching": "Caching",
"Multitenancy": "Multimandantenfähigkeit",
"DataFiltering": "Datenfilterung",
"ConventionOverConfiguration": "Convention Over Configuration",
"ConventionOverConfigurationExplanation": "ABP implementiert standardmäßig allgemeine Anwendungskonventionen mit einer minimalen oder Null-Konfiguration.",
"ConventionOverConfigurationExplanationList1": "Automatische Registrierung bekannter Services für Dependency Injection.",
"ConventionOverConfigurationExplanationList2": "Stellt Anwendungsdienste mittels Namenskonventionen als HTTP-APIs bereit.",
"ConventionOverConfigurationExplanationList3": "Erstellt dynamische HTTP-Client-Proxys für C# und JavaScript.",
"ConventionOverConfigurationExplanationList4": "Bietet Standard-Repositorys für Ihre Entities.",
"ConventionOverConfigurationExplanationList5": "Verwaltet die Unit-of-Work gemäß Webanforderung oder Anwendungsdienstmethode.",
"ConventionOverConfigurationExplanationList6": "Triggert Erstellungs-, Aktualisierungs- und Lösch-Events für Ihre Entities.",
"BaseClasses": "Basisklassen",
"BaseClassesExplanation": "Vorgefertigte Basisklassen für gängige Anwendungsmuster.",
"DeveloperFocusedExplanation": "ABP ist für Entwickler",
"DeveloperFocusedExplanationCont": "Es zielt darauf ab, Ihre tägliche Softwareentwicklung zu vereinfachen, ohne Sie daran zu hindern, Low-Level-Code zu schreiben.",
"SeeAllFeatures": "Alle Features anzeigen",
"CLI_CommandLineInterface": "CLI (Command Line Interface)",
"CLI_CommandLineInterfaceExplanation": "Enthält eine CLI, mit der Sie die Erstellung neuer Projekte und das Hinzufügen neuer Module automatisieren können.",
"StartupTemplates": "Startvorlagen",
"StartupTemplatesExplanation": "Verschiedene Startvorlagen bieten eine vollständig konfigurierte Lösung, um Ihre Entwicklung zu beschleunigen.",
"BasedOnFamiliarTools": "Basierend auf vertrauten Tools",
"BasedOnFamiliarToolsExplanation": "Aufbauend auf und integriert mit beliebten Tools, die Sie bereits kennen. Geringe Lernkurve, einfache Anpassung, komfortable Entwicklung.",
"ORMIndependent": "ORM-unabhängig",
"ORMIndependentExplanation": "Das Kernframework ist ORM-/datenbankunabhängig und kann mit jeder Datenquelle arbeiten. Entity Framework Core- und MongoDB-Anbieter sind bereits verfügbar.",
"Features": "Entdecken Sie die ABP Framework-Features",
"ABPCLI": "ABP CLI",
"Modularity": "Modularität",
"BootstrapTagHelpers": "Bootstrap Tag Helpers",
"DynamicForms": "Dynamische Formulare",
"BundlingMinification": "Bundling & Minification",
"BackgroundJobs": "Background Jobs",
"BackgroundJobsExplanation": "Definieren Sie einfache Klassen, um Jobs im Hintergrund in der Warteschlange auszuführen. Verwenden Sie den integrierten Jobmanager oder integrieren Sie Ihren eigenen. <a href=\"{0}\"> Hangfire </a> & <a href=\"{1}\"> RabbitMQ </a> -Integrationen sind bereits verfügbar.",
"DDDInfrastructure": "DDD-Infrastruktur",
"DomainDrivenDesignInfrastructure": "Domain Driven Design-Infrastruktur",
"AutoRESTAPIs": "Auto REST APIs",
"DynamicClientProxies": "Dynamische Client-Proxies",
"DistributedEventBus": "Distributed Event Bus",
"DistributedEventBusWithRabbitMQIntegration": "Distributed Event Bus mit RabbitMQ-Integration",
"TestInfrastructure": "Test-Infrastruktur",
"AuditLoggingEntityHistories": "Audit Logging & Entity Histories",
"ObjectToObjectMapping": "Object to Object Mapping",
"ObjectToObjectMappingExplanation": "<a href=\"{0}\"> Object to Object Mapping </a> Abstraktion mit AutoMapper-Integration.",
"EmailSMSAbstractions": "E-Mail & SMS Abstraktionen",
"EmailSMSAbstractionsWithTemplatingSupport": "E-Mail- und SMS-Abstraktionen mit Vorlagenunterstützung",
"Localization": "Lokalisierung",
"SettingManagement": "Einstellungsverwaltung",
"ExtensionMethods": "Erweiterungsmethoden",
"ExtensionMethodsHelpers": "Erweiterungsmethoden & Helfer",
"AspectOrientedProgramming": "Aspektorientierte Programmierung",
"DependencyInjection": "Dependency Injection",
"DependencyInjectionByConventions": "Dependency Injection durch Konventionen",
"ABPCLIExplanation": "ABP CLI (Command Line Interface) ist ein Befehlszeilenprogramm zum Ausführen einiger gängiger Vorgänge für ABP-basierte Lösungen.",
"ModularityExplanation": "ABP bietet eine vollständige Infrastruktur zum Erstellen eigener Anwendungsmodule, die Entities, Services, Datenbankintegration, APIs, UI-Komponenten usw. enthalten können.",
"MultiTenancyExplanation": "Das ABP-Framework unterstützt nicht nur die Entwicklung von Multi-Mandantenanwendungen, sondern macht Ihren Code von der Mandantenfähigkeit auch weitgehend unabhängig.",
"MultiTenancyExplanation2": "Kann den aktuellen Mandanten automatisch ermitteln und Daten verschiedener Mandanten voneinander isolieren.",
"MultiTenancyExplanation3": "Unterstützt einzelne Datenbank-, Datenbank-pro-Mandanten- und Hybrid-Ansätze.",
"MultiTenancyExplanation4": "Sie konzentrieren sich auf Ihren geschäftsspezifischen Code und lassen das Framework die Mandantenfähigkeit für Sie übernehmen.",
"BootstrapTagHelpersExplanation": "Anstatt die sich wiederholenden Details von Bootstrap-Komponenten manuell zu schreiben, verwenden Sie die Tag-Helper von ABP, um diese zu vereinfachen und dabei die Vorteile von Intellisense zu nutzen. Sie können Bootstrap weitherhin verwenden, wann immer Sie es benötigen.",
"DynamicFormsExplanation": "Helfer für dynamische Formular- und Input-Tags können das vollständige Formular anhand einer C#-Klasse als Model erstellen.",
"AuthenticationAuthorizationExplanation": "Umfangreiche Authentifizierungs- und Autorisierungsoptionen, die in ASP.NET Core Identity & IdentityServer4 integriert sind. Bietet ein erweiterbares und detailliertes Berechtigungssystem.",
"CrossCuttingConcernsExplanation": "Wiederholen Sie sich nicht, um all diese allgemeinen Dinge immer wieder zu implementieren. Konzentrieren Sie sich auf Ihren geschäftsspezifischen Code und lassen Sie ihn von ABP durch Konventionen automatisieren.",
"DatabaseConnectionTransactionManagement": "Datenbankverbindungs- und Transaktionsmanagement",
"CorrelationIdTracking": "Correlation-ID-Verfolgung",
"BundlingMinificationExplanation": "ABP bietet ein einfaches, dynamisches, leistungsstarkes, modulares und integriertes Bundling- und Minification-System.",
"VirtualFileSystemnExplanation": "Das virtuelle Dateisystem ermöglicht die Verwaltung von Dateien, die physisch nicht auf dem Dateisystem (Datenträger) vorhanden sind. Es wird hauptsächlich verwendet, um Dateien (js, css, image, cshtml ...) in Assemblys einzubetten und sie zur Laufzeit wie physische Dateien zu verwenden.",
"ThemingExplanation": "Mit dem Theming-System können Sie Ihre Anwendung & Module Theme-unabhängig entwickeln, indem Sie eine Reihe gemeinsamer Basisbibliotheken und Layouts definieren, die auf dem neuesten Bootstrap-Framework basieren.",
"DomainDrivenDesignInfrastructureExplanation": "Eine vollständige Infrastruktur zum Erstellen von mehrschichtigen Anwendungen basierend auf den Domain Driven Design Entwurfsmustern und -prinzipien;",
"Specification": "Specification",
"Repository": "Repository",
"DomainService": "Domain Service",
"ValueObject": "Value Object",
"ApplicationService": "Application Service",
"DataTransferObject": "Data Transfer Object",
"AggregateRootEntity": "Aggregate Root, Entity",
"AutoRESTAPIsExplanation": "ABP kann Ihre Anwendungsservices gemäß Konvention automatisch als API-Controller konfigurieren.",
"DynamicClientProxiesExplanation": "Verwenden Sie Ihre APIs ganz einfach in JavaScript- und C#-Clients.",
"DistributedEventBusWithRabbitMQIntegrationExplanation": "Veröffentlichen und konsumieren Sie Distributed Events einfach mithilfe des integrierten Distributed Event Bus mit verfügbarer RabbitMQ-Integration.",
"TestInfrastructureExplanation": "Das Framework wurde unter Berücksichtigung von Unit- und Integrationstests entwickelt. Bietet Ihnen Basisklassen, um es einfacher zu machen. Startvorlagen werden mit vorkonfiguriert Tests geliefert.",
"AuditLoggingEntityHistoriesExplanation": "Integriertes Audit Logging für geschäftskritische Anwendungen. Audit Logging auf Request-, Service-, und Methodenebene sowie Entity-Historien mit Details auf Property-Ebene.",
"EmailSMSAbstractionsWithTemplatingSupportExplanation": "IEmailSender- und ISmsSender-Abstraktionen entkoppeln Ihre Anwendungslogik von der Infrastruktur. Das erweiterte E-Mail-Vorlagensystem ermöglicht das Erstellen und Lokalisieren von E-Mail-Vorlagen und deren einfache Verwendung bei Bedarf.",
"LocalizationExplanation": "Das Lokalisierungssystem ermöglicht das Erstellen von Ressourcen in einfachen JSON-Dateien und die Lokalisierung Ihrer Benutzeroberfläche. Es unterstützt erweiterte Szenarien wie Vererbung, Erweiterungen und JavaScript-Integration und ist vollständig mit dem Lokalisierungssystem von AspNet Core kompatibel.",
"SettingManagementExplanation": "Definieren Sie Einstellungen für Ihre Anwendung und erhalten Sie zur Laufzeit Werte basierend auf der aktuellen Konfiguration, dem Mandanten und dem Benutzer.",
"ExtensionMethodsHelpersExplanation": "Wiederholen Sie sich nicht einmal für triviale Codeteile. Erweiterungen und Helfer für Standardtypen machen Ihren Code viel sauberer und einfacher zu schreiben.",
"AspectOrientedProgrammingExplanation": "Bietet eine komfortable Infrastruktur zum Erstellen dynamischer Proxys und zum Implementieren der aspektorientierten Programmierung. Fangen Sie eine Klasse ab und führen Sie Ihren Code vor und nach jeder Methodenausführung aus.",
"DependencyInjectionByConventionsExplanation": "Sie müssen Ihre Klassen nicht manuell für die Dependency Injection registrieren. Registriert gängige Servicetypen automatisch gemäß Konvention. Für andere Arten von Services können Sie Schnittstellen und Attribute verwenden, um dies einfacher gestalten und an Ort und Stelle zu ermöglichen.",
"DataFilteringExplanation": "Definieren und verwenden Sie Datenfilter, die automatisch angewendet werden, wenn Sie Entities aus der Datenbank abfragen. Soft Delete- und Multimandanten-Filter sind sofort verfügbar, wenn Sie einfache Schnittstellen implementieren.",
"PublishEvents": "Events veröffentlichen",
"HandleEvents": "Auf Events reagieren",
"AndMore": "und mehr...",
"Code": "Code",
"Result": "Resultat",
"SeeTheDocumentForMoreInformation": "Weitere Informationen finden Sie in der <a href=\"{1}\"> {0} -Dokumentation </a>",
"IndexPageHeroSection": "<span class=\"first-line shine\"><strong>Open Source</strong></span><span class=\"second-line text-uppercase\">Webanwendung<br />Framework </span><span class=\"third-line shine2\"><strong>für ASP.Net Core</strong></span>",
"UiFramework": "UI-Framework",
"EmailAddress": "E-Mail-Adresse",
"Mobile": "Mobile",
"ReactNative": "React Native",
"Strong": "Stark",
"Complete": "Vollständig",
"BasedLayeringModel": "Based Layering Model",
"Microservice": "Microservice",
"Compatible": "Kompatibel",
"MeeTTheABPCommunityInfo": "Unsere Mission ist es, eine Umgebung zu schaffen, in der Entwickler sich gegenseitig mit Beiträgen, Tutorials, Fallstudien usw. helfen und Gleichgesinnte treffen können.",
"JoinTheABPCommunityInfo": "Beteiligen Sie sich an einer lebendigen Community und tragen Sie zum ABP Framework bei!",
"AllArticles": "Alle Beiträge",
"SubmitYourArticle": "Reichen Sie Ihren Beitrag ein",
"DynamicClientProxyDocument": "In der Dokumentation zu den Dynamischen Client-Proxies finden Sie Informationen zu <a href=\"{0}\">JavaScript</a> & <a href=\"{1}\">C#</a>.",
"EmailSMSAbstractionsDocument": "Weitere Informationen finden Sie in den Unterlagen <a href=\"{0}\">E-Mail-Senden</a> and <a href=\"{1}\">SMS-Senden</a>.",
"CreateProjectWizard": "Dieser Assistent erstellt ein neues Projekt aus der Startvorlage, die ordnungsgemäß konfiguriert ist, um Ihr Projekt zu starten.",
"TieredOption": "Erstellt eine Tiered Lösung, bei der Web- und HTTP-API-Ebenen physisch getrennt sind. Wenn diese Option nicht aktiviert ist, wird eine mehrschichtige Lösung erstellt, die weniger komplex und für die meisten Szenarien geeignet ist.",
"SeparateIdentityServerOption": "Trennt die Serverseite in zwei Anwendungen: Die erste ist für den Identitätsserver und die zweite für die serverseitige HTTP-API.",
"UseslatestPreVersion": "Verwendet die neueste Vorabversion",
"ReadTheDocumentation": "<span class=\"text-primary\">Lesen Sie</span><span class=\"text-success\">Die Dokumentation</span>",
"Documentation": "Dokumentation",
"GettingStartedTutorial": "Erste Schritte Tutorial",
"ApplicationDevelopmentTutorial": "Tutorial zur Anwendungsentwicklung",
"TheStartupTemplate": "Die Startvorlage",
"InstallABPCLIInfo": "ABP CLI ist der schnellste Weg, um eine neue Lösung mit dem ABP-Framework zu starten. Installieren Sie die ABP-CLI über die Eingabeaufforderung:",
"DifferentLevelOfNamespaces": "Sie können verschiedene Ebenen von Namespaces verwenden; z.B. BookStore, Acme.BookStore or Acme.Retail.BookStore.",
"ABPCLIExamplesInfo": "Der Befehl <strong>new</strong> erstellt eine <strong>mehrschichtige MVC-Anwendung</strong> mit <strong>Entity Framework Core</strong> als Datenbankanbieter. Es gibt jedoch zusätzliche Optionen. Beispiele:",
"SeeCliDocumentForMoreInformation": "Weitere Optionen finden Sie im <a href=\"{0}\">ABP CLI-Dokument</a> oder wählen Sie oben die Registerkarte \"Direkter Download\".",
"Optional": "Optional",
"LocalFrameworkRef": "Behalten Sie die lokale Projektreferenz für die Framework-Pakete bei."
}
}

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

@ -45,7 +45,7 @@
"CompleteArchitectureInfo": "Modern architecture to create maintainable software solutions.",
"DomainDrivenDesignBasedLayeringModelExplanation": "Helps you to implement a DDD based layered architecture and build a maintainable code base.",
"DomainDrivenDesignBasedLayeringModelExplanationCont": "Provides startup templates, abstractions, base classes, services, documentation and guides to help you to develop your application based on DDD patterns & principles.",
"MicroserviceCompatibleModelExplanation": "The core framework & pre-build modules are designed the microservice architecture in mind.",
"MicroserviceCompatibleModelExplanation": "The core framework & pre-build modules are designed with microservice architecture in mind.",
"MicroserviceCompatibleModelExplanationCont": "Provides infrastructure, integrations, samples and documentation to implement microservice solutions easier, while it doesn\u2019t bring additional complexity if you want a monolithic application.",
"ModularInfo": "ABP provides a module system that allows you to develop reusable application modules, tie into application lifecycle events, and express dependencies between core parts of your system.",
"PreBuiltModulesThemes": "Pre-Built Modules & Themes",

189
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/es.json

@ -0,0 +1,189 @@
{
"culture": "es",
"texts": {
"GetStarted": "",
"Create": "",
"NewProject": "",
"DirectDownload": "",
"ProjectName": "",
"ProjectType": "",
"DatabaseProvider": "",
"NTier": "",
"IncludeUserInterface": "",
"CreateNow": "",
"TheStartupProject": "",
"Tutorial": "",
"UsingCLI": "",
"SeeDetails": "",
"AbpShortDescription": "",
"SourceCodeUpper": "",
"LatestReleaseLogs": "",
"Infrastructure": "",
"Architecture": "",
"Modular": "",
"DontRepeatYourself": "",
"DeveloperFocused": "",
"FullStackApplicationInfrastructure": "",
"DomainDrivenDesign": "",
"DomainDrivenDesignExplanation": "",
"Authorization": "",
"AuthorizationExplanation": "",
"MultiTenancy": "",
"MultiTenancyExplanationShort": "",
"CrossCuttingConcerns": "",
"CrossCuttingConcernsExplanationShort": "",
"BuiltInBundlingMinification": "",
"BuiltInBundlingMinificationExplanation": "",
"VirtualFileSystem": "",
"VirtualFileSystemExplanation": "",
"Theming": "",
"ThemingExplanationShort": "",
"BootstrapTagHelpersDynamicForms": "",
"BootstrapTagHelpersDynamicFormsExplanation": "",
"HTTPAPIsDynamicProxies": "",
"HTTPAPIsDynamicProxiesExplanation": "",
"CompleteArchitectureInfo": "",
"DomainDrivenDesignBasedLayeringModelExplanation": "",
"DomainDrivenDesignBasedLayeringModelExplanationCont": "",
"MicroserviceCompatibleModelExplanation": "",
"MicroserviceCompatibleModelExplanationCont": "",
"ModularInfo": "",
"PreBuiltModulesThemes": "",
"PreBuiltModulesThemesExplanation": "",
"NuGetNPMPackages": "",
"NuGetNPMPackagesExplanation": "",
"ExtensibleReplaceable": "",
"ExtensibleReplaceableExplanation": "",
"CrossCuttingConcernsExplanation2": "",
"CrossCuttingConcernsExplanation3": "",
"AuthenticationAuthorization": "",
"ExceptionHandling": "",
"Validation": "",
"DatabaseConnection": "",
"TransactionManagement": "",
"AuditLogging": "",
"Caching": "",
"Multitenancy": "",
"DataFiltering": "",
"ConventionOverConfiguration": "",
"ConventionOverConfigurationExplanation": "",
"ConventionOverConfigurationExplanationList1": "",
"ConventionOverConfigurationExplanationList2": "",
"ConventionOverConfigurationExplanationList3": "",
"ConventionOverConfigurationExplanationList4": "",
"ConventionOverConfigurationExplanationList5": "",
"ConventionOverConfigurationExplanationList6": "",
"BaseClasses": "",
"BaseClassesExplanation": "",
"DeveloperFocusedExplanation": "",
"DeveloperFocusedExplanationCont": "",
"SeeAllFeatures": "",
"CLI_CommandLineInterface": "",
"CLI_CommandLineInterfaceExplanation": "",
"StartupTemplates": "",
"StartupTemplatesExplanation": "",
"BasedOnFamiliarTools": "",
"BasedOnFamiliarToolsExplanation": "",
"ORMIndependent": "",
"ORMIndependentExplanation": "",
"Features": "",
"ABPCLI": "",
"Modularity": "",
"BootstrapTagHelpers": "",
"DynamicForms": "",
"BundlingMinification": "",
"BackgroundJobs": "",
"BackgroundJobsExplanation": "",
"DDDInfrastructure": "",
"DomainDrivenDesignInfrastructure": "",
"AutoRESTAPIs": "",
"DynamicClientProxies": "",
"DistributedEventBus": "",
"DistributedEventBusWithRabbitMQIntegration": "",
"TestInfrastructure": "",
"AuditLoggingEntityHistories": "",
"ObjectToObjectMapping": "",
"ObjectToObjectMappingExplanation": "",
"EmailSMSAbstractions": "",
"EmailSMSAbstractionsWithTemplatingSupport": "",
"Localization": "",
"SettingManagement": "",
"ExtensionMethods": "",
"ExtensionMethodsHelpers": "",
"AspectOrientedProgramming": "",
"DependencyInjection": "",
"DependencyInjectionByConventions": "",
"ABPCLIExplanation": "",
"ModularityExplanation": "",
"MultiTenancyExplanation": "",
"MultiTenancyExplanation2": "",
"MultiTenancyExplanation3": "",
"MultiTenancyExplanation4": "",
"BootstrapTagHelpersExplanation": "",
"DynamicFormsExplanation": "",
"AuthenticationAuthorizationExplanation": "",
"CrossCuttingConcernsExplanation": "",
"DatabaseConnectionTransactionManagement": "",
"CorrelationIdTracking": "",
"BundlingMinificationExplanation": "",
"VirtualFileSystemnExplanation": "",
"ThemingExplanation": "",
"DomainDrivenDesignInfrastructureExplanation": "",
"Specification": "",
"Repository": "",
"DomainService": "",
"ValueObject": "",
"ApplicationService": "",
"DataTransferObject": "",
"AggregateRootEntity": "",
"AutoRESTAPIsExplanation": "",
"DynamicClientProxiesExplanation": "",
"DistributedEventBusWithRabbitMQIntegrationExplanation": "",
"TestInfrastructureExplanation": "",
"AuditLoggingEntityHistoriesExplanation": "",
"EmailSMSAbstractionsWithTemplatingSupportExplanation": "",
"LocalizationExplanation": "",
"SettingManagementExplanation": "",
"ExtensionMethodsHelpersExplanation": "",
"AspectOrientedProgrammingExplanation": "",
"DependencyInjectionByConventionsExplanation": "",
"DataFilteringExplanation": "",
"PublishEvents": "",
"HandleEvents": "",
"AndMore": "",
"Code": "",
"Result": "",
"SeeTheDocumentForMoreInformation": "",
"IndexPageHeroSection": "",
"UiFramework": "",
"EmailAddress": "",
"Mobile": "",
"ReactNative": "",
"Strong": "",
"Complete": "",
"BasedLayeringModel": "",
"Microservice": "",
"Compatible": "",
"MeeTTheABPCommunityInfo": "",
"JoinTheABPCommunityInfo": "",
"AllArticles": "",
"SubmitYourArticle": "",
"DynamicClientProxyDocument": "",
"EmailSMSAbstractionsDocument": "",
"CreateProjectWizard": "",
"TieredOption": "",
"SeparateIdentityServerOption": "",
"UseslatestPreVersion": "",
"ReadTheDocumentation": "",
"Documentation": "",
"GettingStartedTutorial": "",
"ApplicationDevelopmentTutorial": "",
"TheStartupTemplate": "",
"InstallABPCLIInfo": "",
"DifferentLevelOfNamespaces": "",
"ABPCLIExamplesInfo": "",
"SeeCliDocumentForMoreInformation": "",
"Optional": "",
"LocalFrameworkRef": ""
}
}

6
build/common.ps1

@ -16,7 +16,9 @@ $solutionPaths = @(
"../modules/tenant-management",
"../modules/audit-logging",
"../modules/background-jobs",
"../modules/account"
"../modules/account",
"../modules/cms-kit",
"../modules/blob-storing-database"
)
if ($full -eq "-f")
@ -29,6 +31,8 @@ if ($full -eq "-f")
"../modules/blogging",
"../templates/module/aspnet-core",
"../templates/app/aspnet-core",
"../templates/console",
"../templates/wpf",
"../abp_io/AbpIoLocalization"
)
}else{

11
common.DotSettings

@ -20,6 +20,17 @@
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
<s:Boolean x:Key="/Default/Environment/InlayHints/CppTypeNameHintsOptions/HideTypeNameHintsWhenTypeNameIsEvidentFromVariableName/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/Environment/InlayHints/CppTypeNameHintsOptions/ShowMethodReturnTypeNameHints/@EntryValue">Never</s:String>
<s:String x:Key="/Default/Environment/InlayHints/CppTypeNameHintsOptions/ShowTypeNameHintsForImplicitlyTypedVariables/@EntryValue">Never</s:String>
<s:Boolean x:Key="/Default/Environment/InlayHints/CSharpTypeNameHintsOptions/HideTypeNameHintsWhenTypeNameIsEvidentFromVariableName/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/Environment/InlayHints/CSharpTypeNameHintsOptions/ShowMethodReturnTypeNameHints/@EntryValue">Never</s:String>
<s:String x:Key="/Default/Environment/InlayHints/CSharpTypeNameHintsOptions/ShowTypeNameHintsForImplicitlyTypedVariables/@EntryValue">Never</s:String>
<s:String x:Key="/Default/Environment/InlayHints/CSharpTypeNameHintsOptions/ShowTypeNameHintsForLambdaExpressionParameters/@EntryValue">Never</s:String>
<s:String x:Key="/Default/Environment/InlayHints/CSharpTypeNameHintsOptions/ShowTypeNameHintsForLinqQueryRangeVariables/@EntryValue">Never</s:String>
<s:String x:Key="/Default/Environment/InlayHints/CSharpTypeNameHintsOptions/ShowTypeNameHintsForPatternMatchingExpressions/@EntryValue">Never</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002EDaemon_002ETypeNameHints_002ECppTypeNameHintsOptionsMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECSharp_002ETypeNameHints_002ECSharpTypeNameHintsOptionsMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/HideTypeNameHintsWhenTypeNameIsEvidentFromVariableName/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowMethodReturnTypeNameHints/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForImplicitlyTypedVariables/@EntryValue">False</s:Boolean>

8
common.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>3.3.2</Version>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>4.0.2</Version>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>
@ -13,4 +13,4 @@
<ItemGroup>
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" PrivateAssets="All" />
</ItemGroup>
</Project>
</Project>

35
docs/en/API/Auto-API-Controllers.md

@ -75,17 +75,48 @@ Configure<AbpAspNetCoreMvcOptions>(options =>
Then the route for getting a book will be '**/api/volosoft/book-store/book/{id}**'. This sample uses two-level root path, but you generally use a single level depth.
* Continues with the **normalized controller/service name**. Normalization removes 'AppService', 'ApplicationService' and 'Service' postfixes and converts it to **camelCase**. If your application service class name is 'BookAppService' then it becomes only '/book'.
* Continues with the **normalized controller/service name**. Normalization removes 'AppService', 'ApplicationService' and 'Service' postfixes and converts it to **kebab-case**. If your application service class name is 'ReadingBookAppService' then it becomes only '/reading-book'.
* If you want to customize naming, then set the `UrlControllerNameNormalizer` option. It's a func delegate which allows you to determine the name per controller/service.
* If the method has an '**id**' parameter then it adds '**/{id}**' ro the route.
* Then it adds the action name if necessary. Action name is obtained from the method name on the service and normalized by;
* Removing '**Async**' postfix. If the method name is 'GetPhonesAsync' then it becomes 'GetPhones'.
* Removing **HTTP method prefix**. 'GetList', 'GetAll', 'Get', 'Put', 'Update', 'Delete', 'Remove', 'Create', 'Add', 'Insert', 'Post' and 'Patch' prefixes are removed based on the selected HTTP method. So, 'GetPhones' becomes 'Phones' since 'Get' prefix is a duplicate for a GET request.
* Converting the result to **camelCase**.
* Converting the result to **kebab-case**.
* If the resulting action name is **empty** then it's not added to the route. If it's not empty, it's added to the route (like '/phones'). For 'GetAllAsync' method name it will be empty, for 'GetPhonesAsync' method name it will be 'phones'.
* Normalization can be customized by setting the `UrlActionNameNormalizer` option. It's an action delegate that is called for every method.
* If there is another parameter with 'Id' postfix, then it's also added to the route as the final route segment (like '/phoneId').
#### Customizing the Route Calculation
`IConventionalRouteBuilder` is used to build the route. It is implemented by the `ConventionalRouteBuilder` by default and works as explained above. You can replace/override this service to customize the route calculation strategy.
#### Version 3.x Style Route Calculation
The route calculation was different before the version 4.0. It was using camelCase conventions, while the ABP Framework version 4.0+ uses kebab-case. If you use the old route calculation strategy, follow one of the approaches;
* Set `UseV3UrlStyle` to `true` in the options of the `options.ConventionalControllers.Create(...)` method. Example:
````csharp
options.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly, opts =>
{
opts.UseV3UrlStyle = true;
});
````
This approach effects only the controllers for the `BookStoreApplicationModule`.
* Set `UseV3UrlStyle` to `true` for the `AbpConventionalControllerOptions` to set it globally. Example:
```csharp
Configure<AbpConventionalControllerOptions>(options =>
{
options.UseV3UrlStyle = true;
});
```
Setting it globally effects all the modules in a modular application.
## Service Selection
Creating conventional HTTP API controllers are not unique to application services actually.

51
docs/en/API/Dynamic-CSharp-API-Clients.md

@ -1,10 +1,23 @@
# Dynamic C# API Client Proxies
ABP can dynamically create C# API client proxies to call remote HTTP services (REST APIs). In this way, you don't need to deal with `HttpClient` and other low level HTTP features to call remote services and get results.
ABP can dynamically create C# API client proxies to call your remote HTTP services (REST APIs). In this way, you don't need to deal with `HttpClient` and other low level details to call remote services and get results.
Dynamic C# proxies automatically handles the following stuff for you;
* Maps C# **method calls** to remote server **HTTP calls** by considering the HTTP method, route, query string parameters, request payload and other details.
* **Authenticates** the HTTP Client by adding access token to the HTTP header.
* **Serializes** to and deserialize from JSON.
* Handles HTTP API **versioning**.
* Add **correlation id**, current **tenant** id and the current **culture** to the request.
* Properly **handles the error messages** sent by the server and throws proper exceptions.
This system can be used by any type of .NET client to consume your HTTP APIs.
## Service Interface
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project. Example:
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project, typically in the `Application.Contracts` project if you've created your solution using the startup templates.
Example:
````csharp
public interface IBookAppService : IApplicationService
@ -13,7 +26,7 @@ public interface IBookAppService : IApplicationService
}
````
Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition.
> Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition.
Implement this class in your service application. You can use [auto API controller system](Auto-API-Controllers.md) to expose the service as a REST API endpoint.
@ -55,6 +68,8 @@ public class MyClientAppModule : AbpModule
`AddHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, creates and registers proxy classes.
> The startup templates already comes pre-configured for the client proxy generation, in the `HttpApi.Client` project.
### Endpoint Configuration
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. Simplest configuration is shown below:
@ -106,7 +121,7 @@ While you can inject `IBookAppService` like above to use the client proxy, you c
### AbpRemoteServiceOptions
`AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example:
`AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can configure it in the `ConfigureServices` method of your [module](../Module-Development-Basics.md) to set or override it. Example:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
@ -162,4 +177,30 @@ context.Services.AddHttpClientProxies(
Using `asDefaultServices: false` may only be needed if your application has already an implementation of the service and you do not want to override/replace the other implementation by your client proxy.
> If you disable `asDefaultServices`, you can only use `IHttpClientProxy<T>` interface to use the client proxies. See the *IHttpClientProxy Interface* section above.
> If you disable `asDefaultServices`, you can only use `IHttpClientProxy<T>` interface to use the client proxies. See the *IHttpClientProxy Interface* section above.
### Retry/Failure Logic & Polly Integration
If you want to add retry logic for the failing remote HTTP calls for the client proxies, you can configure the `AbpHttpClientBuilderOptions` in the `PreConfigureServices` method of your module class.
**Example: Use the [Polly](https://github.com/App-vNext/Polly) library to re-try 3 times on a failure**
````csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpHttpClientBuilderOptions>(options =>
{
options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) =>
{
clientBuilder.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.WaitAndRetryAsync(
3,
i => TimeSpan.FromSeconds(Math.Pow(2, i))
)
);
});
});
}
````
This example uses the [Microsoft.Extensions.Http.Polly](https://www.nuget.org/packages/Microsoft.Extensions.Http.Polly) package. You also need to import the `Polly` namespace (`using Polly;`) to be able to use the `WaitAndRetryAsync` method.

6
docs/en/Application-Services.md

@ -342,12 +342,12 @@ public class DistrictAppService
{
}
protected override async Task DeleteByIdAsync(DistrictKey id)
protected async override Task DeleteByIdAsync(DistrictKey id)
{
await Repository.DeleteAsync(d => d.CityId == id.CityId && d.Name == id.Name);
}
protected override async Task<District> GetEntityByIdAsync(DistrictKey id)
protected async override Task<District> GetEntityByIdAsync(DistrictKey id)
{
return await AsyncQueryableExecuter.FirstOrDefaultAsync(
Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name)
@ -400,7 +400,7 @@ public class MyPeopleAppService : CrudAppService<Person, PersonDto, Guid>
{
}
protected override async Task CheckDeletePolicyAsync()
protected async override Task CheckDeletePolicyAsync()
{
await AuthorizationService.CheckAsync("...");
}

2
docs/en/AspNet-Boilerplate-Migration-Guide.md

@ -438,7 +438,7 @@ ABP Framework uses and extends ASP.NET Core's [distributed caching abstraction](
### Logging
ASP.NET Boilerplate uses Castle Windsor's [logging facility](http://docs.castleproject.org/Windsor.Logging-Facility.ashx) as an abstraction and supports multiple logging providers including Log4Net (the default one comes with the startup projects) and Serilog. You typically property-inject the logger:
ASP.NET Boilerplate uses Castle Windsor's [logging facility](https://github.com/castleproject/Windsor/blob/master/docs/logging-facility.md) as an abstraction and supports multiple logging providers including Log4Net (the default one comes with the startup projects) and Serilog. You typically property-inject the logger:
````csharp
using Castle.Core.Logging; //1: Import Logging namespace

31
docs/en/Authentication/Social-External-Logins.md

@ -1,32 +1,3 @@
# Social/External Logins
The [Account Module](../Modules/Account.md) has already configured to handle social or external logins out of the box. You can follow the ASP.NET Core documentation to add a social/external login provider to your application.
## Example: Facebook Authentication
Follow the [ASP.NET Core Facebook integration document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to support the Facebook login for your application.
#### Add the NuGet Package
Add the [Microsoft.AspNetCore.Authentication.Facebook](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.Facebook) package to your project. Based on your architecture, this can be `.Web`, `.IdentityServer` (for tiered setup) or `.Host` project.
#### Configure the Provider
Use the `.AddFacebook(...)` extension method in the `ConfigureServices` method of your [module](../Module-Development-Basics.md), to configure the client:
````csharp
context.Services.AddAuthentication()
.AddFacebook(facebook =>
{
facebook.AppId = "...";
facebook.AppSecret = "...";
facebook.Scope.Add("email");
facebook.Scope.Add("public_profile");
});
````
> It would be a better practice to use the `appsettings.json` or the ASP.NET Core User Secrets system to store your credentials, instead of a hard-coded value like that. Follow the [Microsoft's document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to learn the user secrets usage.
## Angular UI
Beginning from the v3.1, the Angular UI uses authorization code flow (as a best practice) to authenticate the user by redirecting to the MVC UI login page. So, even if you are using the Angular UI, social/external login integration is same as explained above and it will work out of the box.
> This document has been moved. See the [Account Module](../Modules/Account.md) documentation.

24
docs/en/Authorization.md

@ -234,7 +234,7 @@ context
When you write this code inside your permission definition provider, it finds the "role deletion" permission of the [Identity Module](Modules/Identity.md) and disabled the permission, so no one can delete a role on the application.
> Tip: It is better to check the value returned by the `GetPermissionOrNull` method since it may return null if the given permission was not defined.
> Tip: It is better to check the value returned by the `GetPermissionOrNull` method since it may return null if the given permission was not defined.
## IAuthorizationService
@ -280,21 +280,11 @@ public async Task CreateAsync(CreateAuthorDto input)
## Check a Permission in JavaScript
You may need to check a policy/permission on the client side.
See the following documents to learn how to re-use the authorization system on the client side:
### MVC UI
For ASP.NET Core MVC / Razor Pages applications, you can use the `abp.auth` API.
**Example: Check if a given permission has been granted for the current user**
```js
abp.auth.isGranted('MyPermissionName');
```
### Angular UI
See the [permission management document](UI/Angular/Permission-Management.md) for the Angular UI.
* [ASP.NET Core MVC / Razor Pages UI: Authorization](UI/AspNetCore/JavaScript-API/Auth.md)
* [Angular UI Authorization](UI/Angular/Permission-Management.md)
* [Blazor UI Authorization](UI/Blazor/Authorization.md)
## Permission Management
@ -354,7 +344,7 @@ public class SystemAdminPermissionValueProvider : PermissionValueProvider
public override string Name => "SystemAdmin";
public override async Task<PermissionGrantResult>
public async override Task<PermissionGrantResult>
CheckAsync(PermissionValueCheckContext context)
{
if (context.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin")
@ -406,5 +396,5 @@ This is already done for the startup template integration tests.
## See Also
* [Permission Management Module](Modules/Permission-Management.md)
* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](API/JavaScript-API/Auth.md)
* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](UI/AspNetCore/JavaScript-API/Auth.md)
* [Permission Management in Angular UI](UI/Angular/Permission-Management.md)

2
docs/en/Background-Workers.md

@ -54,7 +54,7 @@ public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
Timer.Period = 600000; //10 minutes
}
protected override async Task DoWorkAsync(
protected async override Task DoWorkAsync(
PeriodicBackgroundWorkerContext workerContext)
{
Logger.LogInformation("Starting: Setting status of inactive users...");

8
docs/en/Blog-Posts/2020-05-08 v2_7_Release/Post.md

@ -16,7 +16,7 @@ ABP.IO Platform is rapidly growing and we are getting more and more contribution
### Object Extending System
In the last few releases, we've mostly focused on providing ways to extend existing modules when you use them as NuGet/NPM Packages.
In the last few releases, we've mostly focused on providing ways to extend existing modules when you use them as NuGet/NPM Packages.
The Object Extending System allows module developers to create extensible modules and allows application developers to customize and extend a module easily.
@ -43,7 +43,7 @@ ObjectExtensionManager.Instance
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(32) {
MinimumLength = 6
MinimumLength = 6
}
);
});
@ -121,7 +121,7 @@ Just create a class derived from the `ExceptionSubscriber` class in your applica
````csharp
public class MyExceptionSubscriber : ExceptionSubscriber
{
public override async Task HandleAsync(ExceptionNotificationContext context)
public async override Task HandleAsync(ExceptionNotificationContext context)
{
//TODO...
}
@ -244,4 +244,4 @@ We ([Volosoft](https://volosoft.com/) - the core team behind the ABP.IO platform
[ABP Framework](https://abp.io/) provides all the infrastructure and application independent framework features to make you more productive, focus on your own business code and implement software development best practices. It provides you a well defined and comfortable development experience without repeating yourself.
[ABP Commercial](https://commercial.abp.io/) provides pre-built functionalities, themes and tooling to save your time if your requirements involve these functionalities in addition to the premium support for the framework and the pre-built modules.
[ABP Commercial](https://commercial.abp.io/) provides pre-built functionalities, themes and tooling to save your time if your requirements involve these functionalities in addition to the premium support for the framework and the pre-built modules.

45
docs/en/Blog-Posts/2020-10-15 v3_3_Preview/POST.md

@ -2,6 +2,32 @@
We have released the [ABP Framework](https://abp.io/) (and the [ABP Commercial](https://commercial.abp.io/)) `3.3.0-rc.1` today. This blog post introduces the new features and important changes in the new version.
## Get Started with the 3.3 RC.1
If you want to try the version `3.3.0-rc.1` today, follow the steps below;
1) **Upgrade** the ABP CLI to the version `3.3.0-rc.1` using a command line terminal:
````bash
dotnet tool update Volo.Abp.Cli -g --version 3.3.0-rc.1
````
**or install** if you haven't installed before:
````bash
dotnet tool install Volo.Abp.Cli -g --version 3.3.0-rc.1
````
2) Create a **new application** with the `--preview` option:
````bash
abp new BookStore --preview
````
See the [ABP CLI documentation](https://docs.abp.io/en/abp/3.3/CLI) for all the available options.
> You can also use the *Direct Download* tab on the [Get Started](https://abp.io/get-started) page by selecting the Preview checkbox.
## What's new with the ABP Framework 3.3
### The Blazor UI
@ -20,19 +46,19 @@ We are still working on the fundamentals. So, the next version may introduce bre
#### Breaking Changes on the Blazor UI
There are some breaking changes with the Blazor UI. If you've built an application and upgrade it, your application might not properly work. See [the migration guide](https://github.com/abpframework/abp/blob/dev/docs/en/Migration-Guides/BlazorUI-3_3.md) for the changes you need to do after upgrading your application.
There are some breaking changes with the Blazor UI. If you've built an application and upgrade it, your application might not properly work. See [the migration guide](https://docs.abp.io/en/abp/3.3/Migration-Guides/BlazorUI-3_3) for the changes you need to do after upgrading your application.
### Automatic Validation for AntiForgery Token for HTTP APIs
Starting with the version 3.3, all your HTTP API endpoints are **automatically protected** against CSRF attacks, unless you disable it for your application.
Starting with the version 3.3, all your HTTP API endpoints are **automatically protected** against CSRF attacks, unless you disable it for your application. So, no configuration needed, just upgrade the ABP Framework.
[See the documentation](https://github.com/abpframework/abp/blob/dev/docs/en/CSRF-Anti-Forgery.md) to understand why you need it and how ABP Framework solves the problem.
[See the documentation](https://docs.abp.io/en/abp/3.3/CSRF-Anti-Forgery) to if you want to understand why you need it and how ABP Framework solves the problem.
### Rebus Integration Package for the Distributed Event Bus
[Rebus](https://github.com/rebus-org/Rebus) describes itself as "Simple and lean service bus implementation for .NET". There are a lot of integration packages like RabbitMQ and Azure Service Bus for the Rebus. The new [Volo.Abp.EventBus.Rebus](https://www.nuget.org/packages/Volo.Abp.EventBus.Rebus) package allows you to use the Rebus as the [distributed event bus](https://docs.abp.io/en/abp/latest/Distributed-Event-Bus) for the ABP Framework.
See [the documentation](https://github.com/abpframework/abp/blob/dev/docs/en/Distributed-Event-Bus-Rebus-Integration.md) to learn how to use Rebus with the ABP Framework.
See [the documentation](https://docs.abp.io/en/abp/3.3/Distributed-Event-Bus-Rebus-Integration) to learn how to use Rebus with the ABP Framework.
### Async Repository LINQ Extension Methods
@ -180,7 +206,14 @@ There are still missing features and modules. However, we are working on it to h
#### Breaking Changes on the Blazor UI
There are some breaking changes with the Blazor UI. If you've built an application and upgrade it, your application might not properly work. See the [ABP Commercial Blazor UI v 3.3 Migration Guide](https://github.com/abpio/abp-commercial-docs/blob/dev/en/migration-guides/blazor-ui-3_3.md) for the changes you need to do after upgrading your application.
There are some breaking changes with the Blazor UI. If you've built an application and upgrade it, your application might not properly work. See the [ABP Commercial Blazor UI v 3.3 Migration Guide](https://docs.abp.io/en/commercial/3.3/migration-guides/blazor-ui-3_3) for the changes you need to do after upgrading your application.
#### Known Issues
When you create a new project, profile management doesn't work, you get an exception because it can't find the `/libs/cropperjs/css/cropper.min.css` file. To fix the issue;
* Add `"@volo/account": "^3.3.0-rc.1"` to the `package.json` in the `.Host` project.
* Run `yarn` (or `npm install`), then `gulp` on a command line terminal in the root folder of the `.Host` project.
### Multi-Tenant Social Logins
@ -216,7 +249,7 @@ Besides the new features introduced in this post, we've done a lot of small othe
## New Articles
The core ABP Framework team & the community continue to publish new articles on the ABP Community web site. The recently published articles are;
The core ABP Framework team & the community continue to publish new articles on the [ABP Community](https://community.abp.io/) web site. The recently published articles are;
* [Replacing Email Templates and Sending Emails](https://community.abp.io/articles/replacing-email-templates-and-sending-emails-jkeb8zzh) (by [@EngincanV](https://community.abp.io/members/EngincanV))
* [How to Add Custom Properties to the User Entity](https://community.abp.io/articles/how-to-add-custom-property-to-the-user-entity-6ggxiddr) (by [@berkansasmaz](https://community.abp.io/members/berkansasmaz))

47
docs/en/Blog-Posts/2020-10-27 v3_3_Release_Stable/POST.md

@ -0,0 +1,47 @@
# ABP Framework & ABP Commercial 3.3 Final Have Been Released
ABP Framework & ABP Commercial 3.3.0 have been released today.
Since all the new features are already explained in details with the [3.3 RC Announcement Post](https://blog.abp.io/abp/ABP-Framework-ABP-Commercial-v3.3-RC-Have-Been-Released), I will not repeat all the details again. Please read [the RC post](https://blog.abp.io/abp/ABP-Framework-ABP-Commercial-v3.3-RC-Have-Been-Released) for **new feature and changes** you may need to do for your solution while upgrading to the version 3.3.
## Creating New Solutions
You can create a new solution with the ABP Framework version 3.3 by either using the `abp new` command or using the **direct download** tab on the [get started page](https://abp.io/get-started).
> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for details.
## How to Upgrade an Existing Solution
### Install/Update the ABP CLI
First of all, install the ABP CLI or upgrade to the latest version.
If you haven't installed yet:
````bash
dotnet tool install -g Volo.Abp.Cli
````
To update an existing installation:
```bash
dotnet tool update -g Volo.Abp.Cli
```
### ABP UPDATE Command
[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides a handy command to update all the ABP related NuGet and NPM packages in your solution with a single command:
````bash
abp update
````
Run this command in the root folder of your solution. After the update command, check [the RC blog post](https://blog.abp.io/abp/ABP-Framework-ABP-Commercial-v3.3-RC-Have-Been-Released) to learn if you need to make any changes in your solution.
> You may want to see the new [upgrading document](https://docs.abp.io/en/abp/latest/Upgrading).
## About the Next Version: 4.0
The next version will be 4.0 and it will be mostly related to completing the Blazor UI features and upgrading the ABP Framework & ecosystem to the .NET 5.0.
The goal is to complete the version 4.0 with a stable Blazor UI with the fundamental features implemented and publish it just after the Microsoft lunches .NET 5 in this November. The planned 4.0 preview release date is November 11th.

143
docs/en/Blog-Posts/2020-11-12 v4_0_Preview/POST.md

@ -0,0 +1,143 @@
# ABP Framework 4.0 RC Has Been Published based on .NET 5.0!
Today, we have released the [ABP Framework](https://abp.io/) (and the [ABP Commercial](https://commercial.abp.io/)) `4.0.0-rc.1` that is based on the **.NET 5.0**. This blog post introduces the new features and important changes in the new version.
> **The planned release date for the [4.0.0 final](https://github.com/abpframework/abp/milestone/45) version is November 26, 2020**.
## Get Started with the 4.0 RC.1
If you want to try the version `4.0.0-rc.1` today, follow the steps below;
1) **Upgrade** the ABP CLI to the version `4.0.0-rc.1` using a command line terminal:
````bash
dotnet tool update Volo.Abp.Cli -g --version 4.0.0-rc.1
````
**or install** if you haven't installed before:
````bash
dotnet tool install Volo.Abp.Cli -g --version 4.0.0-rc.1
````
2) Create a **new application** with the `--preview` option:
````bash
abp new BookStore --preview
````
See the [ABP CLI documentation](https://docs.abp.io/en/abp/3.3/CLI) for all the available options.
> You can also use the *Direct Download* tab on the [Get Started](https://abp.io/get-started) page by selecting the **Preview checkbox**.
## Migrating From 3.x to 4.0
The version 4.0 comes with some major changes including the **migration from .NET Core 3.1 to .NET 5.0**.
We've prepared a **detailed [migration document](https://docs.abp.io/en/abp/4.0/Migration-Guides/Abp-4_0)** to explain all the changes and the actions you need to take while upgrading your existing solutions.
## What's new with the ABP Framework 4.0
### The Blazor UI
The Blazor UI is now stable and officially supported. The [web application development tutorial](https://docs.abp.io/en/abp/4.0/Tutorials/Part-1?UI=Blazor) has been updated based on the version 4.0.
#### abp bundle command
Introducing the `abp bundle` CLI command to manage static JavaScript & CSS file dependencies of a Blazor application. This command is currently used to add the dependencies to the `index.html` file in the dependency order by respecting to modularity. In the next version it will automatically unify & minify the files. The documentation is being prepared.
#### Removed the JQuery & Bootstrap JavaScript
Removed JQuery & Bootstrap JavaScript dependencies for the Blazor UI.
>There are some other changes in the startup template and some public APIs. Follow the [Migration Guide](https://docs.abp.io/en/abp/4.0/Migration-Guides/Abp-4_0) to apply changes for existing solutions that you're upgrading from the version 3.3. While we will continue to make improvements add new features, we no longer make breaking changes on the existing APIs until the version 5.0.
#### Others
A lot of minor and major improvements have been done for the Blazor UI. Some of them are listed below:
* Implemented `IComponentActivator` to resolve the component from the `IServiceProvider`. So, you can now inject dependencies into the constructor of your razor component.
* Introduced the `AbpComponentBase` base class that you derive your components from. It has useful base properties that you can use in your pages/components.
* Introduced `IUiNotificationService` service to show toast notifications on the UI.
* Improved the `IUiMessageService` to show message & confirmation dialogs.
### System.Text.Json
ABP Framework 4.0 uses the System.Text.Json by default as the JSON serialization library. It, actually, using a hybrid approach: Continues to use the Newtonsoft.Json when it needs to use the features not supported by the System.Text.Json.
Follow the [Migration Guide](https://docs.abp.io/en/abp/4.0/Migration-Guides/Abp-4_0) to learn how to configure to use the Newtonsoft.Json for some specific types or switch back to the Newtonsoft.Json as the default JSON serializer.
### Identity Server 4 Upgrade
ABP Framework upgrades the [IdentityServer4](https://www.nuget.org/packages/IdentityServer4) library from 3.x to 4.1.1 with the ABP Framework version 4.0. IdentityServer 4.x has a lot of changes. Some of them are **breaking changes in the data structure**.
Follow the [Migration Guide](https://docs.abp.io/en/abp/4.0/Migration-Guides/Abp-4_0) to upgrade existing solutions.
### Creating a New Module Inside the Application
ABP CLI has now a command to create a new module and add it to an existing solution. In this way, you can create modular applications easier than before.
Example: Create a *ProductManagement* module into your solution.
````bash
abp add-module ProductManagement --new --add-to-solution-file
````
Execute this command in a terminal in the root folder of your solution. If you don't specify the `--add-to-solution-file` option, then the module projects will not be added to the main solution, but the project references still be added. In this case, you need to open the module's solution to develop the module.
See the [CLI document](https://docs.abp.io/en/abp/4.0/CLI) for other options.
### WPF Startup Template
Introducing the WPF startup template for the ABP Framework. Use the ABP CLI new command to create a new WPF application:
````bash
abp new MyWpfApp -t wpf
````
This is a minimalist, empty project template that is integrated to the ABP Framework.
### New Languages
**Thanks to the contributors** from the ABP Community, the framework modules and the startup template have been localized to **German** language by [Alexander Pilhar](https://github.com/alexanderpilhar) & [Nico Lachmuth](https://github.com/tntwist).
### Other Notes
* Upgraded to Angular 11.
* Since [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library not supports transactions, you can use transactions in unit tests for MongoDB.
## What's new with the ABP Commercial 4.0
### The Blazor UI
The Blazor UI for the ABP Commercial is also becomes stable and feature rich with the version 4.0;
* [ABP Suite](https://commercial.abp.io/tools/suite) now supports to generate CRUD pages for the Blazor UI.
* Completed the [Lepton Theme](https://commercial.abp.io/themes) for the Blazor UI.
* Implemented the [File Management](https://commercial.abp.io/modules/Volo.FileManagement) module for the Blazor UI.
### The ABP Suite
While creating create/edit modals with a navigation property, we had two options: A dropdown to select the target entity and a modal to select the entity by searching with a data table.
Dropdown option now supports **lazy load, search and auto-complete**. In this way, selecting a navigation property becomes much easier and supports large data sets on the dropdown.
**Example: Select an author while creating a new book**
![abp-suite-auto-complete-dropdown](abp-suite-auto-complete-dropdown.png)
With the new version, you can **disable backend code generation** on CRUD page generation. This is especially useful if you want to regenerate the page with a different UI framework, but don't want to regenerate the server side code.
### Identity Server Management UI Revised
Completely revised the Identity Server Management UI based on the IDS 4.x changes.
## About the Next Release
The next feature version, `4.1.0`, will mostly focus on completing the missing documents, fixing bugs, performance optimizations and improving the Blazor UI features. The planned preview release date for the version `4.1.0` is December 10 and the final (stable) version release date is December 24.
Follow the [GitHub milestones](https://github.com/abpframework/abp/milestones) for all the planned ABP Framework version release dates.
## Feedback
Please check out the ABP Framework 4.0.0 RC and [provide feedback](https://github.com/abpframework/abp/issues/new) to help us to release a more stable version. **The planned release date for the [4.0.0 final](https://github.com/abpframework/abp/milestone/45) version is November 26**.

BIN
docs/en/Blog-Posts/2020-11-12 v4_0_Preview/abp-suite-auto-complete-dropdown.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

39
docs/en/CLI.md

@ -41,6 +41,7 @@ Here, the list of all available commands before explaining their details:
* **`login`**: Authenticates on your computer with your [abp.io](https://abp.io/) username and password.
* **`logout`**: Logouts from your computer if you've authenticated before.
* **`build`**: Builds a GIT repository and depending repositories or a single .NET solution.
* **`bundle`**: Generates script and style references for an ABP Blazor project.
### help
@ -106,6 +107,7 @@ abp new Acme.BookStore
* `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder.
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](Entity-Framework-Core-Other-DBMS.md) (after creating the solution).
* `--local-framework-ref --abp-path`: Uses local projects references to the ABP framework instead of using the NuGet packages. This can be useful if you download the ABP Framework source code and have a local reference to the framework from your application.
* `--no-random-port`: Uses template's default ports.
### update
@ -159,6 +161,8 @@ abp add-package Volo.Abp.MongoDB
Adds a [multi-package application module](Modules/Index) to a solution by finding all packages of the module, finding related projects in the solution and adding each package to the corresponding project in the solution.
It can also create a new module for your solution and add it to your solution. See `--new-template` option.
> A business module generally consists of several packages (because of layering, different database provider options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module.
Usage
@ -167,21 +171,29 @@ Usage
abp add-module <module-name> [options]
````
Example:
Examples:
```bash
abp add-module Volo.Blogging
```
* This example add the Volo.Blogging module to the solution.
* This example adds the `Volo.Blogging` module to the solution.
```bash
abp add-module ProductManagement --new --add-to-solution-file
```
* This command creates a fresh new module customized for your solution (named `ProductManagement`) and adds it to your solution.
#### Options
* `--solution` or `-s`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
* `--skip-db-migrations`: For EF Core database provider, it automatically adds a new code first migration (`Add-Migration`) and updates the database (`Update-Database`) if necessary. Specify this option to skip this operation.
* `-sp` or `--startup-project`: Relative path to the project folder of the startup project. Default value is the current folder.
* `--with-source-code`: Downloads the source code of the module to your solution folder and uses local project references instead of NuGet/NPM packages.
* `--add-to-solution-file`: Adds the downloaded module to your solution file, so you will also see the projects of the module when you open the solution on a IDE. (only available when `--with-source-code` is used.)
* `--new`: Creates a fresh new module (customized for your solution) and adds it to your solution.
* `--with-source-code`: Downloads the source code of the module to your solution folder and uses local project references instead of NuGet/NPM packages. This options is always `True` if `--new` is used.
* `--add-to-solution-file`: Adds the downloaded/created module to your solution file, so you will also see the projects of the module when you open the solution on a IDE. (only available when `--with-source-code` is `True`.)
### get-source
@ -383,10 +395,27 @@ abp build --build-name "prod" --dotnet-build-arguments "\"--no-dependencies\""
#### Options
* ```--working-directory``` or ```-w```: Specifies the working directory. This option is useful when the command is executed outside of a GIT repository or when executing directory doesn't contain a .NET solution file.
* ```--working-directory``` or ```-wd```: Specifies the working directory. This option is useful when the command is executed outside of a GIT repository or when executing directory doesn't contain a .NET solution file.
* ```--build-name``` or ```-n```: Specifies a name for the build. This option is useful when same repository is used for more than one different builds.
* ```--dotnet-build-arguments``` or ```-a```: Arguments to pass ```dotnet build``` when building project files. This parameter must be passed like ```"\"{params}\""``` .
* ```--force``` or ```-f```: Forces to build projects even they are not changed from the last successful build.
For more details, see [build command documentation](CLI-BuildCommand.md).
#### bundle
This command generates script and style references for an ABP Blazor project and updates the **index.html** file. It helps developers to manage dependencies required by ABP modules easily. In order ```bundle``` command to work, its **executing directory** or passed ```--working-directory``` parameter's directory must contain a Blazor project file(*.csproj).
Usage:
````bash
abp bundle [options]
````
#### Options
* ```--working-directory``` or ```-wd```: Specifies the working directory. This option is useful when executing directory doesn't contain a Blazor project file.
* ```--force``` or ```-f```: Forces to build project before generating references.
For more details about managing style and script references in Blazor apps, see [Managing Global Scripts & Styles](UI/Blazor/Global-Scripts-Styles.md)

30
docs/en/CSRF-Anti-Forgery.md

@ -25,7 +25,7 @@ ABP Framework provides `[AbpValidateAntiForgeryToken]` and `[AbpAutoValidateAnti
ABP Framework also automates the following infrastructure;
* Server side sets a **special cookie**, named `XSRF-TOKEN` by default, that is used make the antiforgery token value available to the browser. This is **done automatically** (by the [application configuration](Application-Configuration.md) endpoint). Nothing to do in the client side.
* Server side sets a **special cookie**, named `XSRF-TOKEN` by default, that is used make the antiforgery token value available to the browser. This is **done automatically** (by the [application configuration](API/Application-Configuration.md) endpoint). Nothing to do in the client side.
* In the client side, it reads the token from the cookie and sends it in the **HTTP header** (named `RequestVerificationToken` by default). This is implemented for all the supported UI types.
* Server side validates the antiforgery token **only for same and cross site requests** made by the browser. It bypasses the validation for non-browser clients.
@ -117,3 +117,31 @@ You don't need to make anything unless you need to change the `AntiforgeryOption
})
export class AppModule {}
```
**Note:** XSRF-TOKEN is only valid if both frontend application and APIs run on the same domain. Therefore, when you make a request, you should use a relative path.
For example, let's say your APIs is hosted at `https://testdomain.com/ws`
and your angular application is hosted at `https://testdomain.com/admin`
So if your API request should look like this `https://testdomain.com/ws/api/identity/users`
your `environment.prod.ts` has to be as follows:
```typescript
export const environment = {
production: true,
// ....
apis: {
default: {
url: '/ws', // <- just use the context root here
// ...
},
},
} as Config.Environment;
```
Let's talk about why.
First, take a look at [Angular's code](https://github.com/angular/angular/blob/master/packages/common/http/src/xsrf.ts#L81)
It does not intercept any request that starts with `http://` or `https://`. There is a good reason for that. Any cross-site request does not need this token for security. This verification is only valid if the request is made to the same domain from which the web page is served. So, simply put, if you serve everything from a single domain, you just use a relative path.

8
docs/en/Community-Articles/2020-04-19-Customize-the-SignIn-Manager/POST.md

@ -1,6 +1,6 @@
# How to Customize the SignIn Manager for ABP Applications
After creating a new application using the [application startup template](https://docs.abp.io/en/abp/latest/Startup-Templates/Application), you may want extend or change the default behavior of the SignIn Manager for your authentication and registration flow needs. ABP [Account Module](https://docs.abp.io/en/abp/latest/Modules/Account) uses the [Identity Management Module](https://docs.abp.io/en/abp/latest/Modules/Identity) for SignIn Manager and the [Identity Management Module](https://docs.abp.io/en/abp/latest/Modules/Identity) uses default [Microsoft Identity SignIn Manager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) ([see here](https://github.com/abpframework/abp/blob/be32a55449e270d2d456df3dabdc91f3ffdd4fa9/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs#L17)).
After creating a new application using the [application startup template](https://docs.abp.io/en/abp/latest/Startup-Templates/Application), you may want extend or change the default behavior of the SignIn Manager for your authentication and registration flow needs. ABP [Account Module](https://docs.abp.io/en/abp/latest/Modules/Account) uses the [Identity Management Module](https://docs.abp.io/en/abp/latest/Modules/Identity) for SignIn Manager and the [Identity Management Module](https://docs.abp.io/en/abp/latest/Modules/Identity) uses default [Microsoft Identity SignIn Manager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) ([see here](https://github.com/abpframework/abp/blob/be32a55449e270d2d456df3dabdc91f3ffdd4fa9/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs#L17)).
To write your Custom SignIn Manager, you need to extend [Microsoft Identity SignIn Manager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) class and register it to the DI container.
@ -27,7 +27,7 @@ public class CustomSignInManager : Microsoft.AspNetCore.Identity.SignInManager<V
}
````
> It is important to use **Volo.Abp.Identity.IdentityUser** type for SignInManager to inherit, not the AppUser of your application.
> It is important to use **Volo.Abp.Identity.IdentityUser** type for SignInManager to inherit, not the AppUser of your application.
Afterwards you can override any of the SignIn Manager methods you need and add new methods and properties needed for your authentication or registration flow.
@ -38,7 +38,7 @@ In this case we'll be overriding the `GetExternalLoginInfoAsync` method which is
A good way to override a method is copying its [source code](https://github.com/dotnet/aspnetcore/blob/c56aa320c32ee5429d60647782c91d53ac765865/src/Identity/Core/src/SignInManager.cs#L638-L674). In this case, we will be using a minorly modified version of the source code which explicitly shows the namespaces of the methods and properties to help better understanding of the concept.
````csharp
public override async Task<Microsoft.AspNetCore.Identity.ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null)
public async override Task<Microsoft.AspNetCore.Identity.ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null)
{
var auth = await Context.AuthenticateAsync(Microsoft.AspNetCore.Identity.IdentityConstants.ExternalScheme);
var items = auth?.Properties?.Items;
@ -93,4 +93,4 @@ PreConfigure<IdentityBuilder>(identityBuilder =>
## The Source Code
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization).
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization).

14
docs/en/Community-Articles/2020-08-07-Passwordless-Authentication/POST.md

@ -33,7 +33,7 @@ namespace PasswordlessAuthentication.Web
}
//We need to override this method as well.
public override async Task<string> GetUserModifierAsync(string purpose, UserManager<TUser> manager, TUser user)
public async override Task<string> GetUserModifierAsync(string purpose, UserManager<TUser> manager, TUser user)
{
var userId = await manager.GetUserIdAsync(user);
@ -105,7 +105,7 @@ namespace PasswordlessAuthentication.Web.Pages
UserManager = userManager;
_userRepository = userRepository;
}
public ActionResult OnGet()
{
if (!CurrentUser.IsAuthenticated)
@ -115,15 +115,15 @@ namespace PasswordlessAuthentication.Web.Pages
return Page();
}
//added for passwordless authentication
public async Task<IActionResult> OnPostGeneratePasswordlessTokenAsync()
{
var adminUser = await _userRepository.FindByNormalizedUserNameAsync("admin");
var token = await UserManager.GenerateUserTokenAsync(adminUser, "PasswordlessLoginProvider",
"passwordless-auth");
PasswordlessLoginUrl = Url.Action("Login", "Passwordless",
new {token = token, userId = adminUser.Id.ToString()}, Request.Scheme);
@ -238,7 +238,7 @@ namespace PasswordlessAuthentication.Web.Controllers
return Redirect("/");
}
private static IEnumerable<Claim> CreateClaims(IUser user, IEnumerable<string> roles)
{
var claims = new List<Claim>
@ -272,4 +272,4 @@ That's all! We created a passwordless login with 7 steps.
## Source Code
The completed sample is available on [GitHub repository](https://github.com/abpframework/abp-samples/tree/master/PasswordlessAuthentication).
The completed sample is available on [GitHub repository](https://github.com/abpframework/abp-samples/tree/master/PasswordlessAuthentication).

46
docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/POST.md

@ -0,0 +1,46 @@
# How to add a new language to your ABP project?
Adding a new language to your ABP project is pretty simple. Let's add the German language to our ABP project:
1. Go to your solution's root folder and write the following CLI command. This command will generate an empty translation file from English.
```bash
abp translate -c de-DE
```
Check out for [the complete supported culture codes](https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes).
(For internal development `D:\Github\abp` and `D:\Github\volo\abp`)
2. Fill the `target` fields in your target language.
![Fill target fields](language-target.png)
3. Copy `abp-translation.json` your solution's root folder (Do not change the filename!)
4. Run the following command. This command will create the necessary `json` files.
```bash
abp translate --apply
```
5. Open your solution and add the new language to the language list. To do this;
* open `MyProjectNameDomainModule.cs` and in `ConfigureServices` you'll find `Configure<AbpLocalizationOptions>`. If you have `HttpApi.Host` project then you need to add this in `MyProjectNameHttpApiHostModule.cs`
```
options.Languages.Add(new LanguageInfo("de-DE", "de-DE", "Deutsch", "de"));
```
![Add to languages](add-to-languages.png)
The last parameter is the flag icon. You can find the list of flag icons on https://flagicons.lipis.dev/
6. The last step is running the DbMigrator project. It will seed the database for the new language.
![The database table](database-table.png)
Close the IIS Express / Kestrel to invalidate the language cache and run the project. You will see the new language on your website.
![See the final result](website-new-language.png)

BIN
docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/add-to-languages.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/database-table.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/language-target.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
docs/en/Community-Articles/2020-11-02-How-To-Add-New-Language/website-new-language.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

27
docs/en/Contribution/Index.md

@ -2,31 +2,36 @@
ABP is an [open source](https://github.com/abpframework) and community driven project. This guide is aims to help anyone wants to contribute to the project.
## community.abp.io
## ABP Community Website
If you want to write articles or "how to" guides related to the ABP Framework and ASP.NET Core, please submit your article to the [community.abp.io](https://community.abp.io/) web site.
If you want to write **articles** or **how to guides** related to the ABP Framework and ASP.NET Core, please submit your article to the [community.abp.io](https://community.abp.io/) website.
## Code Contribution
You can always send pull requests to the Github repository.
You can always send pull requests to the GitHub repository.
- Clone the [ABP repository](https://github.com/abpframework/abp/) from Github.
- Make the required changes.
- Clone the [ABP repository](https://github.com/abpframework/abp/) from GitHub.
- Build the repository using the `/build/build-all.ps1 -f` for one time.
- Make the necessary changes, including unit/integration tests.
- Send a pull request.
> When you open a solution in Visual Studio, you may need to execute `dotnet restore` in the root folder of the solution for one time, after it is fully opened in the Visual Studio. This is needed since VS can't properly resolves local references to projects out of the solution.
### GitHub Issues
Before making any change, please discuss it on the [Github issues](https://github.com/abpframework/abp/issues). In this way, no other developer will work on the same issue and your PR will have a better chance to be accepted.
### Bug Fixes & Enhancements
#### Bug Fixes & Enhancements
You may want to fix a known bug or work on a planned enhancement. See [the issue list](https://github.com/abpframework/abp/issues) on Github.
### Feature Requests
#### Feature Requests
If you have a feature idea for the framework or modules, [create an issue](https://github.com/abpframework/abp/issues/new) on Github or attend to an existing discussion. Then you can implement it if it's embraced by the community.
## Document Translation
You may want to translate the complete [documentation](https://abp.io/documents/) (including this one) to your mother language. If so, follow these steps:
You may want to translate the complete [documentation](https://docs.abp.io) (including this one) to your mother language. If so, follow these steps:
* Clone the [ABP repository](https://github.com/abpframework/abp/) from Github.
* To add a new language, create a new folder inside the [docs](https://github.com/abpframework/abp/tree/master/docs) folder. Folder names can be "en", "es", "fr", "tr" and so on based on the language (see [all culture codes](https://msdn.microsoft.com/en-us/library/hh441729.aspx)).
@ -35,9 +40,9 @@ You may want to translate the complete [documentation](https://abp.io/documents/
There are some fundamental documents need to be translated before publishing a language on the [ABP documentation web site](https://docs.abp.io):
* Getting Started documents
* Tutorials
* CLI
* Index (Home)
* Getting Started
* Web Application Development Tutorial
A new language is published after these minimum translations have been completed.

52
docs/en/Customizing-Application-Modules-Overriding-Services.md

@ -29,7 +29,7 @@ public class TestAppService : IIdentityUserAppService, ITransientDependency
}
````
The dependency injection system allows to register multiple services for the same interface. The last registered one is used when the interface is injected. It is a good practice to explicitly replace the service.
The dependency injection system allows to register multiple services for the same interface. The last registered one is used when the interface is injected. It is a good practice to explicitly replace the service.
Example:
@ -59,7 +59,6 @@ In most cases, you will want to change one or a few methods of the current imple
### Example: Overriding an Application Service
````csharp
//[RemoteService(IsEnabled = false)] // If you use dynamic controller feature you can disable remote service. Prevent creating duplicate controller for the application service.
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IIdentityUserAppService), typeof(IdentityUserAppService), typeof(MyIdentityUserAppService))]
public class MyIdentityUserAppService : IdentityUserAppService
@ -76,7 +75,7 @@ public class MyIdentityUserAppService : IdentityUserAppService
{
}
public override async Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input)
public async override Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input)
{
if (input.PhoneNumber.IsNullOrWhiteSpace())
{
@ -109,33 +108,33 @@ public class MyIdentityUserManager : IdentityUserManager
{
public MyIdentityUserManager(
IdentityUserStore store,
IIdentityRoleRepository roleRepository,
IIdentityRoleRepository roleRepository,
IIdentityUserRepository userRepository,
IOptions<IdentityOptions> optionsAccessor,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<IdentityUser> passwordHasher,
IEnumerable<IUserValidator<IdentityUser>> userValidators,
IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators,
IEnumerable<IUserValidator<IdentityUser>> userValidators,
IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<IdentityUserManager> logger,
ICancellationTokenProvider cancellationTokenProvider) :
ILogger<IdentityUserManager> logger,
ICancellationTokenProvider cancellationTokenProvider) :
base(store,
roleRepository,
userRepository,
optionsAccessor,
passwordHasher,
userValidators,
userRepository,
optionsAccessor,
passwordHasher,
userValidators,
passwordValidators,
keyNormalizer,
errors,
services,
logger,
keyNormalizer,
errors,
services,
logger,
cancellationTokenProvider)
{
}
public override async Task<IdentityResult> CreateAsync(IdentityUser user)
public async override Task<IdentityResult> CreateAsync(IdentityUser user)
{
if (user.PhoneNumber.IsNullOrWhiteSpace())
{
@ -182,7 +181,7 @@ namespace MyProject.Controllers
}
public override async Task SendPasswordResetCodeAsync(
public async override Task SendPasswordResetCodeAsync(
SendPasswordResetCodeDto input)
{
Logger.LogInformation("Your custom logic...");
@ -197,6 +196,15 @@ This example replaces the `AccountController` (An API Controller defined in the
**`[ExposeServices(typeof(AccountController))]` is essential** here since it registers this controller for the `AccountController` in the dependency injection system. `[Dependency(ReplaceServices = true)]` is also recommended to clear the old registration (even the ASP.NET Core DI system selects the last registered one).
In addition, The `AccountController` will be removed from [`ApplicationModel`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.applicationmodels.applicationmodel.controllers) because it defines `ExposeServicesAttribute`. If you don't want to remove it, you can configure `AbpAspNetCoreMvcOptions`:
```csharp
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.IgnoredControllersOnModelExclusion.AddIfNotContains(typeof(AccountController));
});
```
### Overriding Other Classes
Overriding controllers, framework services, view component classes and any other type of classes registered to dependency injection can be overridden just like the examples above.
@ -214,7 +222,7 @@ Assuming that you've already added a `SocialSecurityNumber` as described in the
You can use the [object extension system](Object-Extensions.md) to add the property to the `IdentityUserDto`. Write this code inside the `YourProjectNameDtoExtensions` class comes with the application startup template:
````csharp
ObjectExtensionManager.Instance
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUserDto, string>(
"SocialSecurityNumber"
);
@ -286,8 +294,8 @@ ObjectExtensionManager.Instance
.AddOrUpdateProperty<string>(
new[]
{
typeof(IdentityUserDto),
typeof(IdentityUserCreateDto),
typeof(IdentityUserDto),
typeof(IdentityUserCreateDto),
typeof(IdentityUserUpdateDto)
},
"SocialSecurityNumber"

10
docs/en/Data-Access.md

@ -1,15 +1,13 @@
# Data Access
## Database Providers
ABP framework was designed as database agnostic. It can work any type of data source by the help of the [repository](Repositories.md) and [unit of work](Unit-Of-Work.md) abstractions. However, currently the following providers are implemented:
ABP framework was designed as database agnostic. It can work any type of data source by the help of the [repository](Repositories.md) and [unit of work](Unit-Of-Work.md) abstractions. Currently, the following providers are implemented as official:
* [Entity Framework Core](Entity-Framework-Core.md) (works with [various DBMS and providers](https://docs.microsoft.com/en-us/ef/core/providers/).)
* [MongoDB](MongoDB.md)
* [Dapper](Dapper.md)
More providers will be added in the future.
## See Also
* [Connection Strings](Connection-Strings.md)
* [Connection Strings](Connection-Strings.md)
* [Data Seeding](Data-Seeding.md)
* [Data Filtering](Data-Filtering.md)

2
docs/en/Data-Filtering.md

@ -32,6 +32,8 @@ namespace Acme.BookStore
> `ISoftDelete` filter is enabled by default and you can not get deleted entities from database unless you explicitly disable it. See the `IDataFilter` service below.
> Soft-delete entities can be hard-deleted when you use `HardDeleteAsync` method on the repositories.
### IMultiTenant
[Multi-tenancy](Multi-Tenancy.md) is an efficient way of creating SaaS applications. Once you create a multi-tenant application, you typically want to isolate data between tenants. Implement `IMultiTenant` interface to make your entity "multi-tenant aware".

1978
docs/en/Domain-Driven-Design-Implementation-Guide.md

File diff suppressed because it is too large

14
docs/en/Domain-Driven-Design.md

@ -10,7 +10,7 @@ ABP framework provides an **infrastructure** to make **Domain Driven Design** ba
> - Basing complex designs on a model of the domain;
> - Initiating a creative collaboration between technical and domain experts to iteratively refine a conceptual model that addresses particular domain problems.
### Layers
## Layers & Building Blocks
ABP follows DDD principles and patterns to achieve a layered application model which consists of four fundamental layers:
@ -19,11 +19,7 @@ ABP follows DDD principles and patterns to achieve a layered application model w
- **Domain Layer**: Includes business objects and the core (domain) business rules. This is the heart of the application.
- **Infrastructure Layer**: Provides generic technical capabilities that support higher layers mostly using 3rd-party libraries.
DDD mostly interest in the **Domain** and the **Application** layers, rather than the Infrastructure and the Presentation layers.
## Contents
See the following documents to learn what ABP Framework provides to you to implement DDD in your project.
DDD mostly interest in the **Domain** and the **Application** layers, rather than the Infrastructure and the Presentation layers. The following documents explains the **infrastructure** provided by the ABP Framework to implement **Building Blocks** of the DDD:
* **Domain Layer**
* [Entities & Aggregate Roots](Entities.md)
@ -34,4 +30,8 @@ See the following documents to learn what ABP Framework provides to you to imple
* **Application Layer**
* [Application Services](Application-Services.md)
* [Data Transfer Objects (DTOs)](Data-Transfer-Objects.md)
* [Unit of Work](Unit-Of-Work.md)
* [Unit of Work](Unit-Of-Work.md)
## The Ultimate DDD Implementation Guide
See the [Implementing Domain Driven Design](Domain-Driven-Design-Implementation-Guide.md) guide as a **complete reference**. The Guide explains the Domain Driven Design and introduces explicit **rules and examples** to give a deep understanding of the **implementation details**.

311
docs/en/Entity-Framework-Core.md

@ -92,7 +92,7 @@ protected override void OnModelCreating(ModelBuilder builder)
b.ToTable("Books");
//Configure the base properties
b.ConfigureByConvention();
b.ConfigureByConvention();
//Configure other properties (if you are using the fluent API)
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
@ -113,7 +113,7 @@ If you have multiple databases in your application, you can configure the connec
[ConnectionStringName("MySecondConnString")]
public class MyDbContext : AbpDbContext<MyDbContext>
{
}
```
@ -174,7 +174,7 @@ public class Book : AggregateRoot<Guid>
}
```
(`BookType` is a simple enum here) And you want to create a new `Book` entity in a [domain service](Domain-Services.md):
(`BookType` is a simple `enum` here and not important) And you want to create a new `Book` entity in a [domain service](Domain-Services.md):
````csharp
public class BookManager : DomainService
@ -221,12 +221,13 @@ public interface IBookRepository : IRepository<Book, Guid>
}
````
You generally want to derive from the `IRepository` to inherit standard repository methods. However, you don't have to. Repository interfaces are defined in the domain layer of a layered application. They are implemented in the data/infrastructure layer (`EntityFrameworkCore` project in a [startup template](https://abp.io/Templates)).
You generally want to derive from the `IRepository` to inherit standard repository methods (while, you don't have to do). Repository interfaces are defined in the domain layer of a layered application. They are implemented in the data/infrastructure layer (`EntityFrameworkCore` project in a [startup template](https://abp.io/Templates)).
Example implementation of the `IBookRepository` interface:
````csharp
public class BookRepository : EfCoreRepository<BookStoreDbContext, Book, Guid>, IBookRepository
public class BookRepository
: EfCoreRepository<BookStoreDbContext, Book, Guid>, IBookRepository
{
public BookRepository(IDbContextProvider<BookStoreDbContext> dbContextProvider)
: base(dbContextProvider)
@ -254,7 +255,7 @@ If you want to replace default repository implementation with your custom reposi
context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
{
options.AddDefaultRepositories();
//Replaces IRepository<Book, Guid>
options.AddRepository<Book, BookRepository>();
});
@ -263,7 +264,7 @@ context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
This is especially important when you want to **override a base repository method** to customize it. For instance, you may want to override `DeleteAsync` method to delete a specific entity in a more efficient way:
````csharp
public override async Task DeleteAsync(
public async override Task DeleteAsync(
Guid id,
bool autoSave = false,
CancellationToken cancellationToken = default)
@ -272,6 +273,278 @@ public override async Task DeleteAsync(
}
````
## Loading Related Entities
Assume that you've an `Order` with a collection of `OrderLine`s and the `OrderLine` has a navigation property to the `Order`:
````csharp
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace MyCrm
{
public class Order : AggregateRoot<Guid>, IHasCreationTime
{
public Guid CustomerId { get; set; }
public DateTime CreationTime { get; set; }
public ICollection<OrderLine> Lines { get; set; } //Sub collection
public Order()
{
Lines = new Collection<OrderLine>();
}
}
public class OrderLine : Entity<Guid>
{
public Order Order { get; set; } //Navigation property
public Guid OrderId { get; set; }
public Guid ProductId { get; set; }
public int Count { get; set; }
public double UnitPrice { get; set; }
}
}
````
And defined the database mapping as shown below:
````csharp
builder.Entity<Order>(b =>
{
b.ToTable("Orders");
b.ConfigureByConvention();
//Define the relation
b.HasMany(x => x.Lines)
.WithOne(x => x.Order)
.HasForeignKey(x => x.OrderId)
.IsRequired();
});
builder.Entity<OrderLine>(b =>
{
b.ToTable("OrderLines");
b.ConfigureByConvention();
});
````
When you query an `Order`, you may want to **include** all the `OrderLine`s in a single query or you may want to **load them later** on demand.
> Actually these are not directly related to the ABP Framework. You can follow the [EF Core documentation](https://docs.microsoft.com/en-us/ef/core/querying/related-data/) to learn all the details. This section will cover some topics related to the ABP Framework.
### Eager Loading / Load With Details
You have different options when you want to load the related entities while querying an entity.
#### Repository.WithDetails
`IRepository.WithDetails(...)` can be used to include one relation collection/property to the query.
**Example: Get an order with lines**
````csharp
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;
namespace MyCrm
{
public class OrderManager : DomainService
{
private readonly IRepository<Order, Guid> _orderRepository;
public OrderManager(IRepository<Order, Guid> orderRepository)
{
_orderRepository = orderRepository;
}
public async Task TestWithDetails(Guid id)
{
var query = _orderRepository
.WithDetails(x => x.Lines)
.Where(x => x.Id == id);
var order = await AsyncExecuter.FirstOrDefaultAsync(query);
}
}
}
````
> `AsyncExecuter` is used to execute async LINQ extensions without depending on the EF Core. If you add EF Core NuGet package reference to your project, then you can directly use `await _orderRepository.WithDetails(x => x.Lines).FirstOrDefaultAsync()`. But, this time you depend on the EF Core in your domain layer. See the [repository document](Repositories.md) to learn more.
**Example: Get a list of orders with their lines**
````csharp
public async Task TestWithDetails()
{
var query = _orderRepository
.WithDetails(x => x.Lines);
var orders = await AsyncExecuter.ToListAsync(query);
}
````
> `WithDetails` method can get more than one expression parameter if you need to include more than one navigation property or collection.
#### DefaultWithDetailsFunc
If you don't pass any expression to the `WithDetails` method, then it includes all the details using the `DefaultWithDetailsFunc` option you provide.
You can configure `DefaultWithDetailsFunc` for an entity in the `ConfigureServices` method of your [module](Module-Development-Basics.md) in your `EntityFrameworkCore` project.
**Example: Include `Lines` while querying an `Order`**
````csharp
Configure<AbpEntityOptions>(options =>
{
options.Entity<Order>(orderOptions =>
{
orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Lines);
});
});
````
> You can fully use the EF Core API here since this is located in the EF Core integration project.
Then you can use the `WithDetails` without any parameter:
````csharp
public async Task TestWithDetails()
{
var query = _orderRepository.WithDetails();
var orders = await AsyncExecuter.ToListAsync(query);
}
````
`WithDetails()` executes the expression you've setup as the `DefaultWithDetailsFunc`.
#### Repository Get/Find Methods
Some of the standard [Repository](Repositories.md) methods have optional `includeDetails` parameters;
* `GetAsync` and `FindAsync` gets `includeDetails` with default value is `true`.
* `GetListAsync` and `GetPagedListAsync` gets `includeDetails` with default value is `false`.
That means, the methods return a **single entity includes details** by default while list returning methods don't include details by default. You can explicitly pass `includeDetails` to change the behavior.
> These methods use the `DefaultWithDetailsFunc` option that is explained above.
**Example: Get an order with details**
````csharp
public async Task TestWithDetails(Guid id)
{
var order = await _orderRepository.GetAsync(id);
}
````
**Example: Get an order without details**
````csharp
public async Task TestWithoutDetails(Guid id)
{
var order = await _orderRepository.GetAsync(id, includeDetails: false);
}
````
**Example: Get list of entities with details**
````csharp
public async Task TestWithDetails()
{
var orders = await _orderRepository.GetListAsync(includeDetails: true);
}
````
#### Alternatives
The repository patters tries to encapsulate the EF Core, so your options are limited. If you need an advanced scenario, you can follow one of the options;
* Create a custom repository method and use the complete EF Core API.
* Reference to the `Volo.Abp.EntityFrameworkCore` package from your project. In this way, you can directly use `Include` and `ThenInclude` in your code.
See also [eager loading document](https://docs.microsoft.com/en-us/ef/core/querying/related-data/eager) of the EF Core.
### Explicit / Lazy Loading
If you don't include relations while querying an entity and later need to access to a navigation property or collection, you have different options.
#### EnsurePropertyLoadedAsync / EnsureCollectionLoadedAsync
Repositories provide `EnsurePropertyLoadedAsync` and `EnsureCollectionLoadedAsync` extension methods to **explicitly load** a navigation property or sub collection.
**Example: Load Lines of an Order when needed**
````csharp
public async Task TestWithDetails(Guid id)
{
var order = await _orderRepository.GetAsync(id, includeDetails: false);
//order.Lines is empty on this stage
await _orderRepository.EnsureCollectionLoadedAsync(order, x => x.Lines);
//order.Lines is filled now
}
````
`EnsurePropertyLoadedAsync` and `EnsureCollectionLoadedAsync` methods do nothing if the property or collection was already loaded. So, calling multiple times has no problem.
See also [explicit loading document](https://docs.microsoft.com/en-us/ef/core/querying/related-data/explicit) of the EF Core.
#### Lazy Loading with Proxies
Explicit loading may not be possible in some cases, especially when you don't have a reference to the `Repository` or `DbContext`. Lazy Loading is a feature of the EF Core that loads the related properties / collections when you first access to it.
To enable lazy loading;
1. Install the [Microsoft.EntityFrameworkCore.Proxies](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Proxies/) package into your project (typically to the EF Core integration project)
2. Configure `UseLazyLoadingProxies` for your `DbContext` (in the `ConfigureServices` method of your module in your EF Core project). Example:
````csharp
Configure<AbpDbContextOptions>(options =>
{
options.PreConfigure<MyCrmDbContext>(opts =>
{
opts.DbContextOptions.UseLazyLoadingProxies(); //Enable lazy loading
});
options.UseSqlServer();
});
````
3. Make your navigation properties and collections `virtual`. Examples:
````csharp
public virtual ICollection<OrderLine> Lines { get; set; } //virtual collection
public virtual Order Order { get; set; } //virtual navigation property
````
Once you enable lazy loading and arrange your entities, you can freely access to the navigation properties and collections:
````csharp
public async Task TestWithDetails(Guid id)
{
var order = await _orderRepository.GetAsync(id);
//order.Lines is empty on this stage
var lines = order.Lines;
//order.Lines is filled (lazy loaded)
}
````
Whenever you access to a property/collection, EF Core automatically performs an additional query to load the property/collection from the database.
> Lazy loading should be carefully used since it may cause performance problems in some specific cases.
See also [lazy loading document](https://docs.microsoft.com/en-us/ef/core/querying/related-data/lazy) of the EF Core.
## Access to the EF Core API
In most cases, you want to hide EF Core APIs behind a repository (this is the main purpose of the repository pattern). However, if you want to access the `DbContext` instance over the repository, you can use `GetDbContext()` or `GetDbSet()` extension methods. Example:
@ -296,7 +569,7 @@ public class BookService
* `GetDbContext` returns a `DbContext` reference instead of `BookStoreDbContext`. You can cast it, however in most cases you don't need it.
> Important: You must reference to the `Volo.Abp.EntityFrameworkCore` package from the project you want to access to the DbContext. This breaks encapsulation, but this is what you want in that case.
> Important: You must reference to the `Volo.Abp.EntityFrameworkCore` package from the project you want to access to the `DbContext`. This breaks encapsulation, but this is what you want in that case.
## Extra Properties & Object Extension Manager
@ -365,7 +638,7 @@ public class MyRepositoryBase<TEntity>
: EfCoreRepository<BookStoreDbContext, TEntity>
where TEntity : class, IEntity
{
public MyRepositoryBase(IDbContextProvider<BookStoreDbContext> dbContextProvider)
public MyRepositoryBase(IDbContextProvider<BookStoreDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
@ -395,7 +668,7 @@ context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
typeof(MyRepositoryBase<,>),
typeof(MyRepositoryBase<>)
);
//...
});
```
@ -446,6 +719,22 @@ context.Services.AddAbpDbContext<OtherDbContext>(options =>
In this example, `OtherDbContext` implements `IBookStoreDbContext`. This feature allows you to have multiple DbContext (one per module) on development, but single DbContext (implements all interfaces of all DbContexts) on runtime.
### Split Queries
ABP enables [split queries](https://docs.microsoft.com/en-us/ef/core/querying/single-split-queries) globally by default for better performance. You can change it as needed.
**Example**
````csharp
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer(optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery);
});
});
````
## See Also
* [Entities](Entities.md)
* [Entities](Entities.md)

10
docs/en/Exception-Handling.md

@ -1,11 +1,11 @@
# Exception Handling
ABP provides a built-in infrastructure and offers a standard model for handling exceptions in a web application.
ABP provides a built-in infrastructure and offers a standard model for handling exceptions.
* Automatically **handles all exceptions** and sends a standard **formatted error message** to the client for an API/AJAX request.
* Automatically hides **internal infrastructure errors** and returns a standard error message.
* Provides a configurable way to **localize** exception messages.
* Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map these to custom exceptions.
* Provides an easy and configurable way to **localize** exception messages.
* Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map custom exceptions.
## Automatic Exception Handling
@ -85,7 +85,7 @@ Error **details** in an optional field of the JSON error message. Thrown `Except
### Logging
Caught exceptions are automatically logged.
Caught exceptions are automatically logged.
#### Log Level
@ -300,7 +300,7 @@ In this case, create a class derived from the `ExceptionSubscriber` class in you
````csharp
public class MyExceptionSubscriber : ExceptionSubscriber
{
public override async Task HandleAsync(ExceptionNotificationContext context)
public async override Task HandleAsync(ExceptionNotificationContext context)
{
//TODO...
}

2
docs/en/Getting-Started-AspNetCore-Application.md

@ -4,7 +4,7 @@ This tutorial explains how to start ABP from scratch with minimal dependencies.
## Create A New Project
1. Create a new AspNet Core Web Application from Visual Studio 2019 (16.4.0+):
1. Create a new AspNet Core Web Application from Visual Studio 2019 (16.8.0+):
![](images/create-new-aspnet-core-application-v2.png)

65
docs/en/Getting-Started-Create-Solution.md

@ -0,0 +1,65 @@
# Getting Started
````json
//[doc-params]
{
"UI": ["MVC", "Blazor", "NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
````
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
## Create a New Project
Use the `new` command of the ABP CLI to create a new project:
````shell
abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u blazor{{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes"}}{{if UI == "MVC"}} --tiered{{else}} --separate-identity-server{{end}}{{end}}
````
*You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.*
{{ if Tiered == "Yes" }}
{{ if UI == "MVC" }}
* `--tiered` argument is used to create N-tiered solution where authentication server, UI and API layers are physically separated.
{{ else }}
* `--separate-identity-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint on the server.
{{ end }}
{{ end }}
> [ABP CLI document](./CLI.md) covers all of the available commands and options.
> Alternatively, you can **create and download** projects from [ABP Framework website](https://abp.io/get-started) by easily selecting the all the options from the page.
### The Solution Structure
The solution has a layered structure (based on the [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects. See the [application template document](Startup-Templates/Application.md) to understand the solution structure in details.
{{ if DB == "Mongo" }}
#### MongoDB Transactions
The [startup template](Startup-templates/Index.md) **disables** transactions in the `.MongoDB` project by default. If your MongoDB server supports transactions, you can enable the it in the *YourProjectMongoDbModule* class's `ConfigureServices` method:
```csharp
Configure<AbpUnitOfWorkDefaultOptions>(options =>
{
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Auto;
});
```
> Or you can delete that code since `Auto` is already the default behavior.
{{ end }}
## Next Step
* [Running the solution](Getting-Started-Running-Solution.md)

4
docs/en/Getting-Started-React-Native.md

@ -27,13 +27,13 @@ You have multiple options to initiate a new React Native project that works with
### 1. Using ABP CLI
ABP CLI is probably the most convenient and flexible way to initiate an ABP solution with a React Native application. Simply [install the ABP CLI](../../CLI.md) and run the following command in your terminal:
ABP CLI is probably the most convenient and flexible way to initiate an ABP solution with a React Native application. Simply [install the ABP CLI](CLI.md) and run the following command in your terminal:
```shell
abp new MyCompanyName.MyProjectName -csf -u <angular or mvc> -m react-native
```
> To see further options in the CLI, please visit the [CLI manual](../../CLI.md).
> To see further options in the CLI, please visit the [CLI manual](CLI.md).
This command will prepare a solution with an **Angular** or an **MVC** (depends on your choice), a **.NET Core**, and a **React Native** project in it.

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

@ -0,0 +1,217 @@
# Getting Started
````json
//[doc-params]
{
"UI": ["MVC", "Blazor", "NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
````
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
## Create the Database
### Connection String
Check the **connection string** in the `appsettings.json` file under the {{if Tiered == "Yes"}}`.IdentityServer` and `.HttpApi.Host` projects{{else}}{{if UI=="MVC"}}`.Web` project{{else}}`.HttpApi.Host` project{{end}}{{end}}
{{ if DB == "EF" }}
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
}
````
The solution is configured to use **Entity Framework Core** with **MS SQL Server** by default. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use any supported DBMS. See [the Entity Framework integration document](Entity-Framework-Core.md) to learn how to [switch to another DBMS](Entity-Framework-Core-Other-DBMS.md).
### Apply the Migrations
The solution uses the [Entity Framework Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli). So, you need to apply migrations to create the database. There are two ways of applying the database migrations.
#### Apply Migrations Using the DbMigrator
The solution comes with a `.DbMigrator` console application which applies migrations and also **seeds the initial data**. It is useful on **development** as well as on **production** environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![db-migrator-output](images/db-migrator-output.png)
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database (with the password is `1q2w3E*`) which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
#### Using EF Core Update-Database Command
Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations.
{{ if UI == "MVC" }}
Right click to the {{if Tiered == "Yes"}}`.IdentityServer`{{else}}`.Web`{{end}} project and select **Set as StartUp project**:
{{ else if UI != "MVC" }}
Right click to the `.HttpApi.Host` project and select **Set as StartUp Project**:
{{ end }}
![set-as-startup-project](images/set-as-startup-project.png)
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command:
![package-manager-console-update-database](images/package-manager-console-update-database.png)
This will create a new database based on the configured connection string.
> **Using the `.DbMigrator` tool is the suggested way**, because it also seeds the initial data to be able to properly run the web application.
>
> If you just use the `Update-Database` command, you will have an empty database, so you can not login to the application since there is no initial admin user in the database. You can use the `Update-Database` command in development time when you don't need to seed the database. However, using the `.DbMigrator` application is easier and you can always use it to migrate the schema and seed the database.
{{ else if DB == "Mongo" }}
````json
"ConnectionStrings": {
"Default": "mongodb://localhost:27017/BookStore"
}
````
The solution is configured to use **MongoDB** in your local computer, so you need to have a MongoDB server instance up and running or change the connection string to another MongoDB server.
### Seed Initial Data
The solution comes with a `.DbMigrator` console application which **seeds the initial data**. It is useful on **development** as well as on **production** environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![db-migrator-output](images/db-migrator-output.png)
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database (with the password is `1q2w3E*`) which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
{{ end }}
## Run the Application
{{ if UI == "MVC" }}
{{ if Tiered == "Yes" }}
> Tiered solutions use **Redis** as the distributed cache. Ensure that it is installed and running in your local computer. If you are using a remote Redis Server, set the configuration in the `appsettings.json` files of the projects below.
1. Ensure that the `.IdentityServer` project is the startup project. Run this application that will open a **login** page in your browser.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
You can login, but you cannot enter to the main application here. This is **just the authentication server**.
2. Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a **Swagger UI** in your browser.
![swagger-ui](images/swagger-ui.png)
This is the HTTP API that is used by the web application.
3. Lastly, ensure that the `.Web` project is the startup project and run the application which will open a **welcome** page in your browser
![mvc-tiered-app-home](images/bookstore-home.png)
Click to the **login** button which will redirect you to the *authentication server* to login to the application:
![bookstore-login](images/bookstore-login.png)
{{ else # Tiered != "Yes" }}
Ensure that the `.Web` project is the startup project. Run the application which will open the **login** page in your browser:
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
![bookstore-login](images/bookstore-login.png)
{{ end # Tiered }}
{{ else # UI != "MVC" }}
### Running the HTTP API Host (Server Side)
{{ if Tiered == "Yes" }}
> Tiered solutions use Redis as the distributed cache. Ensure that it is installed and running in your local computer. If you are using a remote Redis Server, set the configuration in the `appsettings.json` files of the projects below.
Ensure that the `.IdentityServer` project is the startup project. Run the application which will open a **login** page in your browser.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
You can login, but you cannot enter to the main application here. This is just the authentication server.
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
{{ else # Tiered == "No" }}
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
{{ end # Tiered }}
![swagger-ui](images/swagger-ui.png)
You can see the application APIs and test them here. Get [more info](https://swagger.io/tools/swagger-ui/) about the Swagger UI.
{{ end # UI }}
{{ if UI == "Blazor" }}
### Running the Blazor Application (Client Side)
Ensure that the `.Blazor` project is the startup project and run the application.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
Once the application starts, click to the **Login** link on to header, which redirects you to the authentication server to enter a username and password:
![bookstore-login](images/bookstore-login.png)
{{ else if UI == "NG" }}
### Running the Angular Application (Client Side)
Go to the `angular` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com/) package manager while `npm install` will also work)
```bash
yarn
```
Once all node modules are loaded, execute `yarn start` (or `npm start`) command:
```bash
yarn start
```
It may take a longer time for the first build. Once it finishes, it opens the Angular UI in your default browser with the [localhost:4200](http://localhost:4200/) address.
![bookstore-login](images/bookstore-login.png)
{{ end }}
Enter **admin** as the username and **1q2w3E*** as the password to login to the application. The application is up and running. You can start developing your application based on this startup template.
## Mobile Development
If you want to include a [React Native](https://reactnative.dev/) project in your solution, add `-m react-native` (or `--mobile react-native`) argument to project creation command. This is a basic React Native startup template to develop mobile applications integrated to your ABP based backends.
See the [Getting Started with the React Native](Getting-Started-React-Native.md) document to learn how to configure and run the React Native application.
## See Also
* [Web Application Development Tutorial](Tutorials/Part-1.md)
* [Application Startup Template](Startup-Templates/Application.md)

56
docs/en/Getting-Started-Setup-Environment.md

@ -0,0 +1,56 @@
# Getting Started
````json
//[doc-params]
{
"UI": ["MVC", "Blazor", "NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
````
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
## Setup Your Development Environment
First things first! Let's setup your development environment before creating the project.
### Pre-Requirements
The following tools should be installed on your development machine:
* [Visual Studio 2019](https://visualstudio.microsoft.com/vs/) (v16.8+) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). <sup id="a-editor">[1](#f-editor)</sup>
* [.NET Core 5.0+](https://www.microsoft.com/net/download/dotnet-core/)
{{ if UI != "Blazor" }}
* [Node v12 or v14](https://nodejs.org/)
* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[2](#f-yarn)</sup> or npm v6+ (already installed with Node)
{{ end }}
{{ if Tiered == "Yes" }}
* [Redis](https://redis.io/) (the startup solution uses the Redis as the [distributed cache](Caching.md)).
{{ end }}
<sup id="f-editor"><b>1</b></sup> _You can use another editor instead of Visual Studio as long as it supports .NET Core and ASP.NET Core._ <sup>[↩](#a-editor)</sup>
{{ if UI != "Blazor" }}
<sup id="f-yarn"><b>2</b></sup> _Yarn v2 works differently and is not supported._ <sup>[↩](#a-yarn)</sup>
{{ end }}
### Install the ABP CLI
[ABP CLI](./CLI.md) is a command line interface that is used to automate some common tasks for ABP based solutions. First, you need to install the ABP CLI using the following command:
````shell
dotnet tool install -g Volo.Abp.Cli
````
If you've already installed, you can update it using the following command:
````shell
dotnet tool update -g Volo.Abp.Cli
````
## Next Step
* [Creating a new solution](Getting-Started-Create-Solution.md)

301
docs/en/Getting-Started.md

@ -9,303 +9,12 @@
}
````
This tutorial explains how to create a new web application using the [application startup template](Startup-Templates/Application.md).
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
## Contents
## Setup Your Development Environment
First things first! Let's setup your development environment before creating the first project.
### Pre-Requirements
The following tools should be installed on your development machine:
* [Visual Studio 2019](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). <sup id="a-editor">[1](#f-editor)</sup>
* [.NET Core 3.1+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12 or v14](https://nodejs.org/)
* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[2](#f-yarn)</sup> or npm v6+ (already installed with Node)
{{ if Tiered == "Yes" }}
* [Redis](https://redis.io/) (the startup solution uses the Redis as the [distributed cache](Caching.md)).
{{ end }}
<sup id="f-editor"><b>1</b></sup> _You can use another editor instead of Visual Studio as long as it supports .NET Core and ASP.NET Core._ <sup>[↩](#a-editor)</sup>
<sup id="f-yarn"><b>2</b></sup> _Yarn v2 works differently and is not supported._ <sup>[↩](#a-yarn)</sup>
### Install the ABP CLI
[ABP CLI](./CLI.md) is a command line interface that is used to automate some common tasks for ABP based solutions.
> ABP CLI is a free & open source tool for the ABP framework.
First, you need to install the ABP CLI using the following command:
````shell
dotnet tool install -g Volo.Abp.Cli
````
If you've already installed, you can update it using the following command:
````shell
dotnet tool update -g Volo.Abp.Cli
````
## Create a New Project
Use the `new` command of the ABP CLI to create a new project:
````shell
abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u blazor{{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes"}}{{if UI == "MVC"}} --tiered{{else}} --separate-identity-server{{end}}{{end}}
````
> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.
> Alternatively, you can select the "Direct Download" tab from the [ABP Framework web site](https://abp.io/get-started) to create a new solution.
{{ if Tiered == "Yes" }}
{{ if UI == "MVC" }}
* `--tiered` argument is used to create N-tiered solution where authentication server, UI and API layers are physically separated.
{{ else }}
* `--separate-identity-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint on the server.
{{ end }}
{{ end }}
### ABP CLI Commands & Options
[ABP CLI document](./CLI.md) covers all of the available commands and options for the ABP CLI. This document uses the [application startup template](Startup-Templates/Application.md) to create a new web application. See the [ABP Startup Templates](Startup-Templates/Index.md) document for other templates.
### The Solution Structure
The solution has a layered structure (based on the [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects. See the [application template document](Startup-Templates/Application.md) to understand the solution structure in details.
{{ if DB == "Mongo" }}
#### MongoDB Transactions
The [startup template](Startup-templates/Index.md) **disables** transactions in the `.MongoDB` project by default. If your MongoDB server supports transactions, you can enable the it in the *YourProjectMongoDbModule* class:
```csharp
Configure<AbpUnitOfWorkDefaultOptions>(options =>
{
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Auto;
});
```
> Or you can delete this code since this is already the default behavior.
{{ end }}
## Create the Database
### Connection String
Check the **connection string** in the `appsettings.json` file under the {{if Tiered == "Yes"}}`.IdentityServer` and `.HttpApi.Host` projects{{else}}{{if UI=="MVC"}}`.Web` project{{else}}`.HttpApi.Host` project{{end}}{{end}}
{{ if DB == "EF" }}
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
}
````
The solution is configured to use **Entity Framework Core** with **MS SQL Server** by default. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use any supported DBMS. See [the Entity Framework integration document](Entity-Framework-Core.md) to learn how to [switch to another DBMS](Entity-Framework-Core-Other-DBMS.md).
### Apply the Migrations
The solution uses the [Entity Framework Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli). So, you need to apply migrations to create the database. There are two ways of applying the database migrations.
#### Apply Migrations Using the DbMigrator
The solution comes with a `.DbMigrator` console application which applies migrations and also **seeds the initial data**. It is useful on **development** as well as on **production** environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![db-migrator-output](images/db-migrator-output.png)
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database (with the password is `1q2w3E*`) which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
#### Using EF Core Update-Database Command
Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations.
{{ if UI == "MVC" }}
Right click to the {{if Tiered == "Yes"}}`.IdentityServer`{{else}}`.Web`{{end}} project and select **Set as StartUp project**:
{{ else if UI != "MVC" }}
Right click to the `.HttpApi.Host` project and select **Set as StartUp Project**:
{{ end }}
![set-as-startup-project](images/set-as-startup-project.png)
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command:
![package-manager-console-update-database](images/package-manager-console-update-database.png)
This will create a new database based on the configured connection string.
> **Using the `.DbMigrator` tool is the suggested way**, because it also seeds the initial data to be able to properly run the web application.
>
> If you just use the `Update-Database` command, you will have an empty database, so you can not login to the application since there is no initial admin user in the database. You can use the `Update-Database` command in development time when you don't need to seed the database. However, using the `.DbMigrator` application is easier and you can always use it to migrate the schema and seed the database.
{{ else if DB == "Mongo" }}
````json
"ConnectionStrings": {
"Default": "mongodb://localhost:27017/BookStore"
}
````
The solution is configured to use **MongoDB** in your local computer, so you need to have a MongoDB server instance up and running or change the connection string to another MongoDB server.
### Seed Initial Data
The solution comes with a `.DbMigrator` console application which **seeds the initial data**. It is useful on **development** as well as on **production** environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![db-migrator-output](images/db-migrator-output.png)
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database (with the password is `1q2w3E*`) which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
{{ end }}
## Run the Application
{{ if UI == "MVC" }}
{{ if Tiered == "Yes" }}
1. Ensure that the `.IdentityServer` project is the startup project. Run this application that will open a **login** page in your browser.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
You can login, but you cannot enter to the main application here. This is **just the authentication server**.
2. Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a **Swagger UI** in your browser.
![swagger-ui](images/swagger-ui.png)
This is the HTTP API that is used by the web application.
3. Lastly, ensure that the `.Web` project is the startup project and run the application which will open a **welcome** page in your browser
![mvc-tiered-app-home](images/bookstore-home.png)
Click to the **login** button which will redirect you to the *authentication server* to login to the application:
![bookstore-login](images/bookstore-login.png)
{{ else # Tiered != "Yes" }}
Ensure that the `.Web` project is the startup project. Run the application which will open the **login** page in your browser:
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
![bookstore-login](images/bookstore-login.png)
{{ end # Tiered }}
{{ else # UI != "MVC" }}
### Running the HTTP API Host (Server Side)
{{ if Tiered == "Yes" }}
Ensure that the `.IdentityServer` project is the startup project. Run the application which will open a **login** page in your browser.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
You can login, but you cannot enter to the main application here. This is just the authentication server.
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
{{ else # Tiered == "No" }}
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
{{ end # Tiered }}
![swagger-ui](images/swagger-ui.png)
You can see the application APIs and test them here. Get [more info](https://swagger.io/tools/swagger-ui/) about the Swagger UI.
> ##### Authorization for the Swagger UI
>
> Most of the HTTP APIs require authentication & authorization. If you want to test authorized APIs, manually go to the `/Account/Login` page, enter `admin` as the username and `1q2w3E*` as the password to login to the application. Then you will be able to execute authorized APIs too.
{{ end # UI }}
{{ if UI == "Blazor" }}
### Running the Blazor Application (Client Side)
Ensure that the `.Blazor` project is the startup project and run the application.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
Once the application starts, click to the **Login** link on to header, which redirects you to the authentication server to enter a username and password:
![bookstore-login](images/bookstore-login.png)
{{ else if UI == "NG" }}
### Running the Angular Application (Client Side)
Go to the `angular` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com/) package manager while `npm install` will also work)
```bash
yarn
```
Once all node modules are loaded, execute `yarn start` (or `npm start`) command:
```bash
yarn start
```
It may take a longer time for the first build. Once it finishes, it opens the Angular UI in your default browser with the [localhost:4200](http://localhost:4200/) address.
![bookstore-login](images/bookstore-login.png)
{{ end }}
Enter **admin** as the username and **1q2w3E*** as the password to login to the application. The application is up and running. You can start developing your application based on this startup template.
## Mobile Development
If you want to include a [React Native](https://reactnative.dev/) project in your solution, add `-m react-native` (or `--mobile react-native`) argument to project creation command. This is a basic React Native startup template to develop mobile applications integrated to your ABP based backends.
See the [Getting Started with the React Native](Getting-Started-React-Native.md) document to learn how to configure and run the React Native application.
## Next
This tutorial explains how to **create and run** a new web application using the ABP Framework. Follow the steps below;
* [Web Application Development Tutorial](Tutorials/Part-1.md)
1. [Setup your development environment](Getting-Started-Setup-Environment)
2. [Creating a new solution](Getting-Started-Create-Solution.md)
3. [Running the solution](Getting-Started-Running-Solution.md)

68
docs/en/Index.md

@ -1,31 +1,73 @@
# ABP Documentation
ABP is an **open source application framework** focused on **ASP.NET Core** based **web application development**. It also supports developing other type of applications.
Explore the navigation menu to deep dive in the documentation.
ABP Framework is a complete **infrastructure** based on the **ASP.NET Core** to create **modern web applications** and **APIs** by following the software development **best practices** and the **latest technologies**.
## Getting Started
The easiest way to start a new web application with the ABP Framework is to use the [getting started](Getting-Started.md) guide.
* [Getting Started Guide](Getting-Started.md) is the easiest way to start a new web application with the ABP Framework.
* [Web Application Development Tutorial](Tutorials/Part-1.md) is a complete tutorial to develop a full stack web application.
## Tutorials / Articles
### UI Framework Options
### Web Application Development
<img width="500" src="images/ui-options.png">
[Web application development tutorial](Tutorials/Part-1.md) is a complete tutorial to develop a full stack application using the ABP Framework.
### Database Provider Options
### ABP Community Articles
<img width="500" src="images/db-options.png">
See also the [ABP Community](https://community.abp.io/) articles.
## Exploring the Documentation
## Samples
ABP has a **comprehensive documentation** that not only explains the ABP Framework, but also includes **guides** and **samples** to help you on creating a **maintainable solution** by introducing and discussing common **software development principle and best practices**.
See the [sample projects](Samples/Index.md) built with the ABP Framework.
### Architecture
ABP offers a complete, modular and layered software architecture based on [Domain Driven Design](Domain-Driven-Design.md) principles and patterns. It also provides the necessary infrastructure to implement this architecture.
* See the [Modularity](Module-Development-Basics.md) document to understand the module system.
* [Implementing Domain Driven Design](Domain-Driven-Design-Implementation-Guide.md) document is an ultimate guide for who want to understand and implement the DDD.
* [Microservice Architecture](Microservice-Architecture.md) document explains how ABP helps to create a microservice solution.
### Infrastructure
There are a lot of features provided by the ABP Framework to achieve real world scenarios easier, like [Event Bus](Event-Bus.md), [Background Job System](Background-Jobs.md), [Audit Logging](Audit-Logging.md), [BLOB Storing](Blob-Storing.md), [Data Seeding](Data-Seeding.md), [Data Filtering](Data-Filtering.md).
### Cross Cutting Concerns
ABP also simplifies (and even automates wherever possible) cross cutting concerns and common non-functional requirements like [Exception Handling](Exception-Handling.md), [Validation](Validation.md), [Authorization](Authorization.md), [Localization](Localization.md), [Caching](Caching.md), [Dependency Injection](Dependency-Injection.md), [Setting Management](Settings.md), etc.
### Application Modules
Application Modules provides pre-built application functionalities;
* [**Account**](Modules/Account.md): Provides UI for the account management and allows user to login/register to the application.
* **[Identity](Modules/Identity.md)**: Manages organization units, roles, users and their permissions, based on the Microsoft Identity library.
* [**IdentityServer**](Modules/IdentityServer.md): Integrates to IdentityServer4.
* [**Tenant Management**](Modules/Tenant-Management.md): Manages tenants for a [multi-tenant](Multi-Tenancy.md) (SaaS) application.
## Source Code
See the [Application Modules](Modules/Index.md) document for all pre-built modules.
### Startup Templates
The [Startup templates](Startup-Templates/Index.md) are pre-built Visual Studio solution templates. You can create your own solution based on these templates to **immediately start your development**.
## ABP Community
### The Source Code
ABP is hosted on GitHub. See [the source code](https://github.com/abpframework).
## Want to Contribute?
### ABP Community Web Site
The [ABP Community](https://community.abp.io/) is a website to publish articles and share knowledge about the ABP Framework. You can also create content for the community!
### Blog
Follow the [ABP Blog](https://blog.abp.io/) to learn the latest happenings in the ABP Framework.
### Samples
See the [sample projects](Samples/Index.md) built with the ABP Framework.
### Want to Contribute?
ABP is a community-driven open source project. See [the contribution guide](Contribution/Index.md) if you want to be a part of this project.

4
docs/en/Localization.md

@ -162,8 +162,8 @@ Getting the localized text is pretty standard.
Just inject the `IStringLocalizer<TResource>` service and use it like shown below:
````C#
public class MyService
````csharp
public class MyService : ITransientDependency
{
private readonly IStringLocalizer<TestResource> _localizer;

121
docs/en/Migration-Guides/Abp-4_0-Angular.md

@ -0,0 +1,121 @@
# Angular UI 3.3 to 4.0 Migration Guide
## Angular v11
The new ABP Angular UI is based on Angular v11 and TypeScript v4. The difference between v10 and v11 is non-breaking so you do not have to update right away but it is recommended. Nevertheless, ABP modules will keep working with Angular v10. Therefore, if your project is Angular v10, you do not need to update to Angular 11. The update is usually very easy though.
You can read more about Angular v11 [here](https://blog.angular.io/version-11-of-angular-now-available-74721b7952f7)
## **Breaking Changes**
### **Localization**
Prior to ABP 4.x, we'd handled what locale files of Angular should be created to load them lazily. However, this made it impossible to add new locale files (to be lazily loaded) for our users. With ABP 4.x, we enabled an option to pass a function to `CoreModule`.
The quickest solution is as follows:
```typescript
// app.module.ts
import { registerLocale } from '@abp/ng.core/locale';
// or
// import { registerLocale } from '@volo/abp.ng.language-management/locale';
// if you have commercial license
@NgModule({
imports: [
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale()
}),
//...
]
export class AppModule {}
```
You can find the related issue [here](https://github.com/abpframework/abp/issues/6066)
Also, please refer to [the docs](https://docs.abp.io/en/abp/latest/UI/Angular/Localization#registering-a-new-locale) for more information.
### **Removed the Angular Account Module Public UI**
With ABP 4.x, we have retired `@abp/ng.account`, it is no longer a part of our framework. There won't be any newer versions of this package as well. Therefore, you can delete anything related to this package.
There should be a config in `app-routing.module` for path `account` and `AccountConfigModule` import in `app.module`
However, if you are using the commercial version of this package, a.k.a `@volo/abp.ng.account`, this package will continue to exist because it contains `AccountAdminModule` which is still being maintained and developed. You only need to delete the route config from `app-routing.module`
You can find the related issue [here](https://github.com/abpframework/abp/issues/5652)
Angular UI is using the Authorization Code Flow to authenticate since version 3.1.0 by default. Starting from version 4.0, this is becoming the only option, because it is the recommended way of authenticating SPAs.
If you haven't done it yet, see [this post](https://blog.abp.io/abp/ABP-Framework-v3.1-RC-Has-Been-Released) to change the authentication of your application.
### State Management
In the ABP Angular UI, we've been using `NGXS` for state management. However, we've decided that the Angular UI should be agnostic with regard to state management. Our users should be able to handle the state in any way they prefer. They should be able to use any library other than `NGXS` or no library at all. That's why we have created our internal store in version 3.2. It is a simple utility class that employs `BehaviorSubject` internally.
You can examine it [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/utils/internal-store-utils.ts)
With version 4.0, we will keep utilizing our `InternalStore` instead of `@ngxs/store` in our services and move away from `@ngxs/store`. We plan to remove any dependency of `NGXS` by version 5.0.
With this in mind, we've already deprecated some services and implemented some breaking changes.
#### Removed the `SessionState`
Use `SessionStateService` instead of the `SessionState`. See [this issue](https://github.com/abpframework/abp/issues/5606) for details.
#### Deprecated the `ConfigState`
`ConfigState` is now deprecated and should not be used.
`ConfigState` reference removed from `CoreModule`. If you want to use the `ConfigState` (not recommended), you should pass the state to `NgxsModule` as shown below:
```typescript
//app.module.ts
import { ConfigState } from '@abp/ng.core';
// ...
imports: [
NgxsModule.forRoot([ConfigState]),
// ...
```
Moving away from the global store, we create small services with a single responsibility. There are two new services available in version 4.0 which are `EnvironmentService` and `PermissionService`.
See [the related issue](https://github.com/abpframework/abp/issues/6154)
Please refer to the following docs for detail information and examples
- [`ConfigStateService`](../UI/Angular/Config-State-Service)
- [`EnvironmentService`](../UI/Angular/Environment#EnvironmentService)
- [`PermissionService`](../UI/Angular/Permission-Management#)
### Deprecated Interfaces
Some interfaces have long been marked as deprecated and now they are removed.
- Removed replaceable components state.
- Removed legacy identity types and service.
- Removed legacy tenant management types and service.
- Removed legacy feature management types and services.
- Removed legacy permission management types and service.
### Deprecated commercial interfaces
- Removed legacy audit logging types and services.
- Removed legacy identity types and services.
- Removed legacy language management types and services.
- Removed legacy saas types and services.
### Identity Server [COMMERCIAL]
With the new version of Identity Server, there happened some breaking changes in the backend (also in the database). We've implemented those in the Angular UI.
If you are just using the package `@volo/abp.ng.identity-server` as is, you will not need to do anything.
However, there are a couple of breaking changes we need to mention.
- As we have stated above, we want to remove the dependency of `NGXS`. Thus, we have deleted all of the actions defined in `identity-server.actions`. Those actions are not needed anymore and the state is managed locally. With the actions gone, `IdentityServerStateService` became unused and got deleted as well.
- `ApiScope` is also available as a new entity (It was part of `ApiResource` before). It provides tokens for entity prop, entity actions, toolbar, edit and create form contributors like the existing ones which are `Client`, `IdentityResource` and `ApiResource`
- There were some deprecated interfaces within the `IdentityServer` namespace. Those are no longer being used, instead, their replacements were generated by `ABP Cli` using the `generate-proxy` command.

88
docs/en/Migration-Guides/Abp-4_0-Blazor.md

@ -0,0 +1,88 @@
# Blazor UI 3.3 to 4.0 Migration Guide
## Startup Template Changes
These changes are required to manually applied in your own solution. It would be easier if you create a new solution based on 4.0 with the same name of your current solution then compare the files.
### Csproj File / Dependencies
* Add `<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>` to the `PropertyGroup` section of your project (`.csproj`) file.
* Update the `Blazorise.*` packages to the latest version (to the latest RC for the ABP 4.0 preview).
### wwwroot/index.html
There are some changes made in the index.html file;
* Removed JQuery & Bootstrap JavaScript dependencies
* Replaced Bootstrap and FontAwesome imports with local files instead of CDN usages.
* Re-arranged some ABP CSS file locations.
* Introduced the `abp bundle` CLI command to manage global Style/Script file imports.
Follow the steps below to apply the changes;
1. Add the bundle contributor class into your project (it will be slightly different based on your solution namespaces):
````csharp
using Volo.Abp.Bundling;
namespace MyCompanyName.MyProjectName.Blazor
{
public class MyProjectNameBundleContributor : IBundleContributor
{
public void AddScripts(BundleContext context)
{
}
public void AddStyles(BundleContext context)
{
context.Add("main.css");
}
}
}
````
If you are using another global style/script files, add them here.
2. Remove all the `<link...>` elements and replace with the following comment tags:
````html
<!--ABP:Styles-->
<!--/ABP:Styles-->
````
3. Remove all the `<script...>` elements and replace with the following comment tags:
````html
<!--ABP:Scripts-->
<!--/ABP:Scripts-->
````
4. Execute the following command in a terminal in the root folder of the Blazor project (`.csproj`) file (ensure that you're using the ABP CLI version 4.0):
````bash
abp bundle
````
This will fill in the `Styles` and `Scripts` tags based on the dependencies.
5. You can clean the `blazor-error-ui` related sections from your `main.css` file since they are not needed anymore.
### The Root Element
This change is optional but recommended.
* Change `<app>...</app>` to `<div id="ApplicationContainer">...</div>` in the `wwwroot/index.html`.
* Change `builder.RootComponents.Add<App>("app");` to `builder.RootComponents.Add<App>("#ApplicationContainer");` in the *YourProjectBlazorModule.cs*.
## AbpCrudPageBase Changes
If you've derived your pages from the `AbpCrudPageBase` class, then you may need to apply the following changes;
- `OpenEditModalAsync` method gets `EntityDto` instead of id (`Guid`) parameter. Pass `context` instead of `context.Id`.
- `DeleteEntityAsync` method doesn't display confirmation dialog anymore. You can use the new `EntityActions` component in Data Grids to show confirmation messages. You can also inject `IUiMessageService` to your page or component and call the `ConfirmAsync` explicitly.
- Added `GetListInput` as a base property that is used to filter while getting the entities from the server.
## Others
- Refactored namespaces for some Blazor components ([#6015](https://github.com/abpframework/abp/issues/6015)).
- Removed Async Suffix from IUiMessageService methods ([#6123](https://github.com/abpframework/abp/pull/6123)).

6
docs/en/Migration-Guides/Abp-4_0-MVC-Razor-Pages.md

@ -0,0 +1,6 @@
# MVC / Razor Pages UI 3.3 to 4.0 Migration Guide
## Use IBrandingProvider in the Volo.Abp.UI Package
This will be a breaking change for MVC UI, but very easy to fix. `IBrandingProvider` is being moved from `Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components` to `Volo.Abp.Ui.Branding` namespace. So, just update the namespace imports.

280
docs/en/Migration-Guides/Abp-4_0.md

@ -0,0 +1,280 @@
# ABP Framework 3.3 to 4.0 Migration Guide
This document introduces the breaking changes done in the ABP Framework 4.0 and explains how to fix your 3.x based solutions while upgrading to the ABP Framework 4.0.
> See [the blog post](https://blog.abp.io/abp/ABP.IO-Platform-v4.0-RC-Has-Been-Released-based-on-.NET-5.0) to learn what's new with the ABP Framework 4.0. This document only focuses on the breaking changes.
## Overall
Here, the overall list of the changes;
* Upgraded to the .NET 5.0 [(#6118](https://github.com/abpframework/abp/issues/6118)).
* Moved from Newtonsoft.Json to System.Text.Json [(#1198](https://github.com/abpframework/abp/issues/1198)).
* Upgraded to the Identity Server 4.1.1 ([#4461](https://github.com/abpframework/abp/issues/4461)).
* Switched to `kebab-case` for conventional URLs for the auto API controller routes ([#5325](https://github.com/abpframework/abp/issues/5325)).
* Removed Retry for the Dynamic HTTP Client Proxies ([#6090](https://github.com/abpframework/abp/issues/6090)).
* Creation audit properties of the entities made read-only ([#6020](https://github.com/abpframework/abp/issues/6020)).
* Changed type of the IHasExtraProperties.ExtraProperties ([#3751](https://github.com/abpframework/abp/issues/3751)).
* Use IBrandingProvider in the Volo.Abp.UI package and remove the one in the Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared ([#5375](https://github.com/abpframework/abp/issues/5375)).
* Removed the Angular Account Module Public UI (login, register... pages) since they are not being used in the default (authorization code) flow ([#5652](https://github.com/abpframework/abp/issues/5652)).
* Removed the SessionState in the @abp/ng.core package ([#5606](https://github.com/abpframework/abp/issues/5606)).
* Made some API revisions & startup template changes for the Blazor UI.
## Upgraded to .NET 5.0
ABP Framework has been moved to .NET 5.0. So, if you want to upgrade to the ABP Framework 4.0, you also need to upgrade to .NET 5.0.
See the [Migrate from ASP.NET Core 3.1 to 5.0](https://docs.microsoft.com/en-us/aspnet/core/migration/31-to-50) document to learn how to upgrade your solution to .NET 5.0.
## Moved to System.Text.Json
ABP Framework 4.0 uses the System.Text.Json by default as the JSON serialization library. It, actually, using a hybrid approach: Continues to use the Newtonsoft.Json when it needs to use features not supported by the System.Text.Json.
### Unsupported Types
If you want to use the Newtonsoft.Json to serialize/deserialize for some specific types, you can configure the `AbpSystemTextJsonSerializerOptions` in your module's `ConfigureServices` method.
**Example: Use Newtonsoft.Json for `MySpecialClass`**
````csharp
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.UnsupportedTypes.AddIfNotContains(typeof(MySpecialClass));
});
````
### Always Use the Newtonsoft.Json
If you want to continue to use the Newtonsoft.Json library for all the types, you can set `UseHybridSerializer` to false in the `PreConfigureServices` method of your module class:
````csharp
PreConfigure<AbpJsonOptions>(options =>
{
options.UseHybridSerializer = false;
});
````
## Upgraded to Identity Server 4.1.1
ABP Framework upgrades the [IdentityServer4](https://www.nuget.org/packages/IdentityServer4) library from 3.x to 4.1.1 with the ABP Framework version 4.0. IdentityServer 4.x has a lot of changes. Some of them are **breaking changes in the data structure**.
### Entity Changes
Entity changes don't directly affect your application; however, it is good to know.
#### ApiScope
As the **most critical breaking change**; Identity Server 4.x defines the `ApiScope` as an independent aggregate root. Previously, it was the child entity of the `ApiResource`. This change requires manual operation. See the _Database Changes_ section.
Also, added `Enabled(string)` and `Description(bool,true)` properties.
#### ApiResource
- Added `AllowedAccessTokenSigningAlgorithms (string)` and `ShowInDiscoveryDocument(bool, default: true)` properties
#### Client
- Added `RequireRequestObject <bool>` and `AllowedIdentityTokenSigningAlgorithms <string>` properties.
- Changed the default value of `RequireConsent` from `true` to `false`.
- Changed the default value of `RequirePkce` from `false` to `true`.
#### DeviceFlowCodes
- Added `SessionId <string>` and `Description <string>` properties.
#### PersistedGrant
- Added `SessionId <string>`, `Description <string>` and `ConsumedTime <DateTime?>` properties
### Database Changes
> Attention: **Please backup your database** before the migration!
**If you are upgrading from 3.x, then there are some steps should be done in your database.**
#### Database Schema Migration
If you are using **Entity Framework Core**, you need to add a new database migration, using the `Add-Migration` command, and apply changes to the database. Please **review the migration** script and read the sections below to understand if it affects your existing data. Otherwise, you may **lose some of your configuration**, which may not be easy to remember and re-configure.
#### Seed Code
If you haven't customized the `IdentityServerDataSeedContributor` and haven't customized the initial data inside the `IdentityServer*` tables;
1. Update `IdentityServerDataSeedContributor` class by comparing to [the latest code](https://github.com/abpframework/abp/blob/dev/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/IdentityServer/IdentityServerDataSeedContributor.cs). You probably only need to add the `CreateApiScopesAsync` method and the code related to it.
2. Then you can simply clear all the **data** in these tables then execute the `DbMigrator` application to fill it with the new configuration.
#### Migrating the Configuration Data
If you've customized your IdentityServer configuration in the database or in the seed data, you should understand the changes and upgrade your code/data accordingly. Especially, the following changes will affect your application:
- `IdentityServerApiScopes` table's `Enabled` field is dropped and re-created. So, you need to enable the API scopes again manually.
- `IdentityServerApiResourceScopes` table is dropped and recreated. So, you need to backup and move your current data to the new table.
- `IdentityServerIdentityResourceClaims` table is dropped and recreated. So, you need to backup and move your current data to the new table.
You may need to perform additional steps based on how much you made custom configurations.
### Other IdentityServer Changes
IdentityServer has removed the [public origin option](https://github.com/IdentityServer/IdentityServer4/pull/4335). It was resolving HTTP/HTTPS conversion issues, but they decided to leave this to the developer. This is especially needed if you use a reverse proxy where your external protocol is HTTPS but internal protocol is HTTP.
One simple solution is to add such a middleware at the begingning of your ASP.NET Core pipeline.
```csharp
app.Use((httpContext, next) =>
{
httpContext.Request.Scheme = "https";
return next();
});
```
> This sample is obtained from the [ASP.NET Core documentation](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#scenarios-and-use-cases). You can use it if you always use HTTPS in all environments.
### Related Resources
- https://leastprivilege.com/2020/06/19/announcing-identityserver4-v4-0/
- https://github.com/IdentityServer/IdentityServer4/issues/4592
## Auto API Controller Route Changes
The route calculation for the [Auto API Controllers](https://docs.abp.io/en/abp/latest/API/Auto-API-Controllers) is changing with the ABP Framework version 4.0 ([#5325](https://github.com/abpframework/abp/issues/5325)). Before v4.0 the route paths were **camelCase**. After version 4.0, it's changed to **kebab-case** route paths where it is possible.
**A typical auto API before v4.0**
![route-before-4](images/route-before-4.png)
**camelCase route parts become kebab-case with 4.0**
![route-4](images/route-4.png)
### How to Fix?
You may not take any action for the MVC & Blazor UI projects.
For the Angular UI, this change may effect your client UI. If you have used the [ABP CLI Service Proxy Generation](../UI/Angular/Service-Proxies.md), you can run the server side and re-generate the service proxies. If you haven't used this tool, you should manually update the related URLs in your application.
If there are other type of clients (e.g. 3rd-party companies) using your APIs, they also need to update the URLs.
### Use the v3.x style URLs
If it is hard to change it in your application, you can still to use the version 3.x route strategy, by following one of the approaches;
- Set `UseV3UrlStyle` to `true` in the options of the `options.ConventionalControllers.Create(...)` method. Example:
```csharp
options.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly, opts =>
{
opts.UseV3UrlStyle = true;
});
```
This approach affects only the controllers for the `BookStoreApplicationModule`.
- Set `UseV3UrlStyle` to `true` for the `AbpConventionalControllerOptions` to set it globally. Example:
```csharp
Configure<AbpConventionalControllerOptions>(options =>
{
options.UseV3UrlStyle = true;
});
```
Setting it globally affects all the modules in a modular application.
## Removed Retry for the Dynamic HTTP Client Proxies
[Dynamic C# HTTP Client Proxies](../API/Dynamic-CSharp-API-Clients.md) were trying up to 3 times if a request fails using the [Polly](https://github.com/App-vNext/Polly) library. Starting from the version 4.0, this logic has been removed. If you need it, you should configure it in your own application, by configuring the `AbpHttpClientBuilderOptions` in the `PreConfigureServices` method of your module.
**Example: Retry 3 times on failure by incremental waiting between tries**
````csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpHttpClientBuilderOptions>(options =>
{
options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) =>
{
clientBuilder.AddTransientHttpErrorPolicy(
policyBuilder => policyBuilder
.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i)))
);
});
});
}
````
This example uses the Microsoft.Extensions.Http.Polly NuGet package.
If you create a new solution, you can find the same configuration in the `.HttpApi.Client.ConsoleTestApp` project's module class, as an example.
## Creation Audit Properties Made Read-Only
Removed setters from the `IHasCreationTime.CreationTime`, ` IMustHaveCreator.CreatorId` and `IMayHaveCreator.CreatorId` properties to accidently set the creation properties while updating an existing entity.
Since the ABP Framework automatically sets these properties, you normally don't need to directly set them. If you want to set them, as a best practice, it is suggested to make it in the constructor to not provide a way to change it later.
These properties implemented with `protected set` in the `Entity` and `AggregateRoot` base classes. That means you can still set in a derived class, if you need it. Alternatively, you can use reflection to set them (Or use `ObjectHelper.TrySetProperty` which internally uses reflection) out of the class if you have to do.
## Changed type of the IHasExtraProperties.ExtraProperties
`IHasExtraProperties.ExtraProperties` was a regular `Dictionary<string, object>`. With the version 4.0, it is replaced with `ExtraPropertyDictionary` class which inherits the `Dictionary<string, object>`.
Most of the applications don't be affected by this change. If you've directly implemented this interface, replace the standard dictionary to the `ExtraPropertyDictionary`.
## Other Changes
### IdentityOptions Usage
Previously, when you inject `IOptions<IdentityOptions>`, you get a dynamically overridden options value. For example, when you get `IdentityOptions.Password.RequiredLength`, the value is being changed based on the setting (`IdentitySettingNames.Password.RequiredLength`) of the current tenant. That means `IdentityOptions` changes per tenant. However, this caused an [issue](https://github.com/abpframework/abp/issues/6318) and we [had to change](https://github.com/abpframework/abp/pull/6333) the usage.
With the version 4.0, you need to inject `IOptions<IdentityOptions>` and call the new `SetAsync` method before using it, to be able to override the options by the settings. Otherwise, you get the default (statically configured) values of the options.
Example usage:
````csharp
public class MyService : ITransientDependency
{
private readonly IOptions<IdentityOptions> _options;
public MyService(IOptions<IdentityOptions> options)
{
_options = options;
}
public async Task DoItAsync()
{
await _options.SetAsync();
var requiredLength = _options.Value.Password.RequiredLength;
}
}
````
Pre-built modules already handles this. However, if you have used `IdentityOptions` directly in your code, you also need to follow this new pattern.
Please make sure that the injected `IOptions<IdentityOptions>` service and the service consuming it are in the same scope of dependency injection container.
### LDAP module full async
In order to solve the problem of async over sync, `ILdapManager` uses async method instead of sync. And use [`ldap4net`](https://github.com/flamencist/ldap4net) to replace [`Novell.Directory.Ldap.NETStandard`](https://github.com/dsbenghe/Novell.Directory.Ldap.NETStandard) package.
### Dynamic external login provider system
You need to change the `WithDynamicOptions` method and pass the `Handler` class of the external login provider.
Use the `goto definition` function in Visual Studio or Rider to check `Handler` in the extension method like `AddGoogle`.
```csharp
- WithDynamicOptions<GoogleOptions>()
+ WithDynamicOptions<GoogleOptions, GoogleHandler>()
````
## ASP.NET Core MVC / Razor Pages UI
See the [ASP.NET Core MVC / Razor Pages UI Migration Guide](Abp-4_0-MVC-Razor-Pages.md).
## Angular UI
See the [Angular UI Migration Guide](Abp-4_0-Angular.md).
## Blazor UI
See the [Blazor UI Migration Guide](Abp-4_0-Blazor.md).

5
docs/en/Migration-Guides/Index.md

@ -0,0 +1,5 @@
# ABP Framework Migration Guides
* [3.3.x to 4.0 Migration Guide](Abp-4_0.md)
* [2.9.x to 3.0 Migration Guide](../UI/Angular/Migration-Guide-v3.md)

BIN
docs/en/Migration-Guides/images/route-4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/en/Migration-Guides/images/route-before-4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

392
docs/en/Module-Entity-Extensions.md

@ -1,3 +1,393 @@
# Module Entity Extensions
See https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions (it will be moved here soon).
## Introduction
Module entity extension system is a **high level** extension system that allows you to **define new properties** for existing entities of the depended modules. It automatically **adds properties to the entity, database, HTTP API and the user interface** in a single point.
> The module must be developed the *Module Entity Extensions* system in mind. All the **official modules** supports this system wherever possible.
## Quick Example
Open the *YourProjectNameModuleExtensionConfigurator* class inside the `Domain.Shared` project of your solution and change the `ConfigureExtraProperties`method as shown below to add a `SocialSecurityNumber` property to the `IdentityUser` entity of the [Identity Module](Modules/Identity.md).
````csharp
public static void ConfigureExtraProperties()
{
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance.Modules()
.ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>( //property type: string
"SocialSecurityNumber", //property name
property =>
{
//validation rules
property.Attributes.Add(new RequiredAttribute());
property.Attributes.Add(
new StringLengthAttribute(64) {
MinimumLength = 4
}
);
//...other configurations for this property
}
);
});
});
});
}
````
>This method is called inside the `YourProjectNameDomainSharedModule` at the beginning of the application. `OneTimeRunner` is a utility class that guarantees to execute this code only one time per application, since multiple calls are unnecessary.
* `ObjectExtensionManager.Instance.Modules()` is the starting point to configure a module. `ConfigureIdentity(...)` method is used to configure the entities of the Identity Module.
* `identity.ConfigureUser(...)` is used to configure the user entity of the identity module. Not all entities are designed to be extensible (since it is not needed). Use the intellisense to discover the extensible modules and entities.
* `user.AddOrUpdateProperty<string>(...)` is used to add a new property to the user entity with the `string` type (`AddOrUpdateProperty` method can be called multiple times for the same property of the same entity. Each call can configure the options of the same property, but only one property is added to the entity with the same property name). You can call this method with different property names to add more properties.
* `SocialSecurityNumber` is the name of the new property.
* `AddOrUpdateProperty` gets a second argument (the `property =>` lambda expression) to configure additional options for the new property.
* We can add data annotation attributes like shown here, just like adding a data annotation attribute to a class property.
#### Create & Update Forms
Once you define a property, it appears in the create and update forms of the related entity:
![add-new-property-to-user-form](images/add-new-property-to-user-form.png)
`SocialSecurityNumber` field comes into the form. Next sections will explain the localization and the validation for this new property.
### Data Table
New properties also appear in the data table of the related page:
![add-new-property-to-user-form](images/add-new-property-to-user-table.png)
`SocialSecurityNumber` column comes into the table. Next sections will explain the option to hide this column from the data table.
## Property Options
There are some options that you can configure while defining a new property.
### Display Name
You probably want to set a different (human readable) display name for the property that is shown on the user interface.
#### Don't Want to Localize?
If your application is not localized, you can directly set the `DisplayName` for the property to a `FixedLocalizableString` object. Example:
````csharp
property =>
{
property.DisplayName = new FixedLocalizableString("Social security no");
}
````
#### Localizing the Display Name
If you want to localize the display name, you have two options.
##### Localize by Convention
Instead of setting the `property.DisplayName`, you can directly open your localization file (like `en.json`) and add the following entry to the `texts` section:
````json
"SocialSecurityNumber": "Social security no"
````
Define the same `SocialSecurityNumber` key (the property name you've defined before) in your localization file for each language you support. That's all!
In some cases, the localization key may conflict with other keys in your localization files. In such cases, you can use the `DisplayName:` prefix for display names in the localization file (`DisplayName:SocialSecurityNumber` as the localization key for this example). Extension system looks for prefixed version first, then fallbacks to the non prefixed name (it then fallbacks to the property name if you haven't localized it).
> This approach is recommended since it is simple and suitable for most scenarios.
##### Localize using the `DisplayName` Property
If you want to specify the localization key or the localization resource, you can still set the `DisplayName` option:
````csharp
property =>
{
property.DisplayName =
LocalizableString.Create<MyProjectNameResource>(
"UserSocialSecurityNumberDisplayName"
);
}
````
* `MyProjectNameResource` is the localization resource and `UserSocialSecurityNumberDisplayName` is the localization key in the localization resource.
> See [the localization document](Localization.md) if you want to learn more about the localization system.
#### Default Value
A default value is automatically set for the new property, which is the natural default value for the property type, like `null` for `string`, `false` for `bool` or `0` for `int`.
There are two ways to override the default value:
##### DefaultValue Option
`DefaultValue` option can be set to any value:
````csharp
property =>
{
property.DefaultValue = 42;
}
````
##### DefaultValueFactory Options
`DefaultValueFactory` can be set to a function that returns the default value:
````csharp
property =>
{
property.DefaultValueFactory = () => DateTime.Now;
}
````
`options.DefaultValueFactory` has a higher priority than the `options.DefaultValue` .
> Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option.
### Validation
Entity extension system allows you to define validation for extension properties in a few ways.
#### Data Annotation Attributes
`Attributes` is a list of attributes associated to this property. The example code below adds two [data annotation validation attributes](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation) to the property:
````csharp
property =>
{
property.Attributes.Add(new RequiredAttribute());
property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4});
}
````
When you run the application, you see that the validation works out of the box:
![add-new-propert-to-user-form](images/add-new-property-to-user-form-validation-error.png)
Since we've added the `RequiredAttribute`, it doesn't allow to left it blank. The validation system works;
* On the user interface (with automatic localization).
* On the HTTP API. Even if you directly perform an HTTP request, you get validation errors with a proper HTTP status code.
* On the `SetProperty(...)` method on the entity (see [the document](Entities.md) if you wonder what is the `SetProperty()` method).
So, it automatically makes a full stack validation.
> See the [ASP.NET Core MVC Validation document](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation) to learn more about the attribute based validation.
##### Default Validation Attributes
There are some attributes **automatically added** when you create certain type of properties;
* `RequiredAttribute` is added for **non nullable** primitive property types (e.g. `int`, `bool`, `DateTime`...) and `enum` types. If you want to allow nulls, make the property nullable (e.g. `int?`).
* `EnumDataTypeAttribute` is added for **enum types**, to prevent to set invalid enum values.
Use `property.Attributes.Clear();` if you don't want these attributes.
#### Validation Actions
Validation actions allows you to execute a custom code to perform the validation. The example below checks if the `SocialSecurityNumber` starts with `B` and adds a validation error if so:
````csharp
property =>
{
property.Attributes.Add(new RequiredAttribute());
property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4});
property.Validators.Add(context =>
{
if (((string) context.Value).StartsWith("B"))
{
context.ValidationErrors.Add(
new ValidationResult(
"Social security number can not start with the letter 'B', sorry!",
new[] {"extraProperties.SocialSecurityNumber"}
)
);
}
});
}
````
Using a `RegularExpressionAttribute` might be better in this case, but this is just an example. Anyway, if you enter a value starts with the letter `B` you get the following error **while saving the form**:
![add-new-propert-to-user-form](images/add-new-property-to-user-form-validation-error-custom.png)
##### The Context Object
The `context` object has useful properties that can be used in your custom validation action. For example, you can use the `context.ServiceProvider` to resolve services from the [dependency injection system](Dependency-Injection.md). The example below gets the localizer and adds a localized error message:
````csharp
if (((string) context.Value).StartsWith("B"))
{
var localizer = context.ServiceProvider
.GetRequiredService<IStringLocalizer<MyProjectNameResource>>();
context.ValidationErrors.Add(
new ValidationResult(
localizer["SocialSecurityNumberCanNotStartWithB"],
new[] {"extraProperties.SocialSecurityNumber"}
)
);
}
````
>`context.ServiceProvider` is nullable! It can be `null` only if you use the `SetProperty(...)` method on the object. Because DI system is not available on this time. While this is a rare case, you should perform a fallback logic when `context.ServiceProvider` is `null`. For this example, you would add a non-localized error message. This is not a problem since setting an invalid value to a property generally is a programmer mistake and you mostly don't need to localization in this case. In any way, you would not be able to use localization even in a regular property setter. But, if you are serious about localization, you can throw a business exception (see the [exception handling document](https://docs.abp.io/en/abp/latest/Exception-Handling) to learn how to localize a business exception).
### UI Visibility
When you define a property, it appears on the data table, create and edit forms on the related UI page. However, you can control each one individually. Example:
````csharp
property =>
{
property.UI.OnTable.IsVisible = false;
//...other configurations
}
````
Use `property.UI.OnCreateForm` and `property.UI.OnEditForm` to control forms too. If a property is required, but not added to the create form, you definitely get a validation exception, so use this option carefully. But a required property may not be in the edit form if that's your requirement.
### HTTP API Availability
Even if you disable a property on UI, it can be still available through the HTTP API. By default, a property is available on all APIs.
Use the `property.Api` options to make a property unavailable in some API endpoints.
````csharp
property =>
{
property.Api.OnUpdate.IsAvailable = false;
}
````
In this example, Update HTTP API will not allow to set a new value to this property. In this case, you also want to disable this property on the edit form:
````csharp
property =>
{
property.Api.OnUpdate.IsAvailable = false;
property.UI.OnEditForm.IsVisible = false;
}
````
In addition to the `property.Api.OnUpdate`, you can set `property.Api.OnCreate` and `property.Api.OnGet` for a fine control the API endpoint.
## Special Types
### Enum
Module extension system naturally supports the `enum` types.
An example enum type:
````csharp
public enum UserType
{
Regular,
Moderator,
SuperUser
}
````
You can add enum properties just like others:
````csharp
user.AddOrUpdateProperty<UserType>("Type");
````
An enum properties is shown as combobox (select) in the create/edit forms:
![add-new-property-enum](images/add-new-property-enum.png)
#### Localization
Enum member name is shown on the table and forms by default. If you want to localize it, just create a new entry on your [localization](https://docs.abp.io/en/abp/latest/Localization) file:
````json
"UserType.SuperUser": "Super user"
````
One of the following names can be used as the localization key:
* `Enum:UserType.SuperUser`
* `UserType.SuperUser`
* `SuperUser`
Localization system searches for the key with the given order. Localized text are used on the table and the create/edit forms.
## Database Mapping
For relational databases, all extension property values are stored in a single field in the table:
![add-new-propert-to-user-database-extra-properties](images/add-new-propert-to-user-database-extra-properties.png)
`ExtraProperties` field stores the properties as a JSON object. While that's fine for some scenarios, you may want to create a dedicated field for your new property. Fortunately, it is very easy to configure.
If you are using the Entity Framework Core database provider, you can configure the database mapping as shown below:
````csharp
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
"SocialSecurityNumber",
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(64);
}
);
````
Write this inside the `YourProjectNameEfCoreEntityExtensionMappings` class in your `.EntityFrameworkCore` project. Then you need to use the standard `Add-Migration` and `Update-Database` commands to create a new database migration and apply the change to your database.
Add-Migration create a new migration as shown below:
````csharp
public partial class Added_SocialSecurityNumber_To_IdentityUser : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SocialSecurityNumber",
table: "AbpUsers",
maxLength: 128,
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SocialSecurityNumber",
table: "AbpUsers");
}
}
````
Once you update your database, you will see that the `AbpUsers` table has the new property as a standard table field:
![add-new-propert-to-user-database-extra-properties](images/add-new-propert-to-user-database-field.png)
> If you first created a property without a database table field, then you later needed to move this property to a database table field, it is suggested to execute an SQL command in your migration to copy the old values to the new field.
>
> However, if you don't make it, the ABP Framework seamlessly manages it. It uses the new database field, but fallbacks to the `ExtraProperties` field if it is null. When you save the entity, it moves the value to the new field.
See the [Extending Entities](Customizing-Application-Modules-Extending-Entities.md) document for more.
## More
See the [Customizing the Modules](Customizing-Application-Modules-Guide.md) guide for an overall index for all the extensibility options.
Here, a few things you can do:
* You can create a second entity that maps to the same database table with the extra property as a standard class property (if you've defined the EF Core mapping). For the example above, you can add a `public string SocialSecurityNumber {get; set;}` property to the `AppUser` entity in your application, since the `AppUser` entity is mapped to the same `AbpUser` table. Do this only if you need it, since it brings more complexity to your application.
* You can override a domain or application service to perform custom logics with your new property.
* You can low level control how to add/render a field in the data table on the UI.

37
docs/en/Modules/Account.md

@ -1,3 +1,38 @@
# Account Module
TODO
This module provides necessary UI pages/components to make the user login and register to the application.
> This document is incomplete.
## Social/External Logins
The [Account Module](../Modules/Account.md) has already configured to handle social or external logins out of the box. You can follow the ASP.NET Core documentation to add a social/external login provider to your application.
### Example: Facebook Authentication
Follow the [ASP.NET Core Facebook integration document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to support the Facebook login for your application.
#### Add the NuGet Package
Add the [Microsoft.AspNetCore.Authentication.Facebook](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.Facebook) package to your project. Based on your architecture, this can be `.Web`, `.IdentityServer` (for tiered setup) or `.Host` project.
#### Configure the Provider
Use the `.AddFacebook(...)` extension method in the `ConfigureServices` method of your [module](../Module-Development-Basics.md), to configure the client:
````csharp
context.Services.AddAuthentication()
.AddFacebook(facebook =>
{
facebook.AppId = "...";
facebook.AppSecret = "...";
facebook.Scope.Add("email");
facebook.Scope.Add("public_profile");
});
````
> It would be a better practice to use the `appsettings.json` or the ASP.NET Core User Secrets system to store your credentials, instead of a hard-coded value like that. Follow the [Microsoft's document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to learn the user secrets usage.
### Other UI Types
Beginning from the v3.1, the [Angular UI](../UI/Angular/Quick-Start.md) uses authorization code flow (as a best practice) to authenticate the user by redirecting to the MVC UI login page. So, even if you are using the Angular UI, social/external login integration is same as explained above and it will work out of the box. As similar, The [Blazor UI](../UI/Blazor/Overall.md) also uses the MVC UI to logic.

3
docs/en/Modules/Client-Simulation.md

@ -0,0 +1,3 @@
# Client Simulation Module
TODO

3
docs/en/Modules/Cms-Kit.md

@ -0,0 +1,3 @@
# CMS Kit Module
TODO

4
docs/en/Modules/Docs.md

@ -446,7 +446,7 @@ As an example you can see ABP Framework documentation:
#### Conditional sections feature (Using Scriban)
Docs module uses [Scriban](<https://github.com/lunet-io/scriban/tree/master/doc> ) for conditionally show or hide some parts of a document. In order to use that feature, you have to create a JSON file as **Parameter document** per every language. It will contain all the key-values, as well as their display names.
Docs module uses [Scriban](https://github.com/lunet-io/scriban/tree/master/doc) for conditionally show or hide some parts of a document. In order to use that feature, you have to create a JSON file as **Parameter document** per every language. It will contain all the key-values, as well as their display names.
For example, [en/docs-params.json](https://github.com/abpio/abp-commercial-docs/blob/master/en/docs-params.json):
@ -537,7 +537,7 @@ Also, **Document_Language_Code** and **Document_Version** keys are pre-defined i
------
**IMPORTANT NOTICE**: Scriban uses "{{" and "}}" for syntax. Therefore, you must use escape blocks if you are going to use those in your document (an Angular document, for example). See [Scriban docs](<https://github.com/lunet-io/scriban/blob/master/doc/language.md#13-escape-block> ) for more information.
**IMPORTANT NOTICE**: Scriban uses "{{" and "}}" for syntax. Therefore, you must use escape blocks if you are going to use those in your document (an Angular document, for example). See [Scriban docs](https://github.com/lunet-io/scriban/blob/master/doc/language.md#13-escape-block) for more information.
### 8- Creating the Navigation Document

52
docs/en/Modules/Identity.md

@ -1,8 +1,8 @@
# Identity Management Module
Identity module is used to manage [organization units](Organization-Units.md), roles, users and their permissions, based on the Microsoft Identity library.
Identity module is used to manage organization units, roles, users and their permissions, based on the Microsoft Identity library.
**See [the source code](https://github.com/abpframework/abp/tree/dev/modules/identity). Documentation will come soon...**
> **See [the source code](https://github.com/abpframework/abp/tree/dev/modules/identity). Documentation will come soon...**
## Identity Security Log
@ -27,3 +27,51 @@ Configure<AbpSecurityLogOptions>(options =>
options.ApplicationName = "AbpSecurityTest";
});
```
## Organization Unit Management
Organization units (OU) is a part of **Identity Module** and can be used to **hierarchically group users and entities**.
### OrganizationUnit Entity
An OU is represented by the **OrganizationUnit** entity. The fundamental properties of this entity are:
- **TenantId**: Tenant's Id of this OU. Can be null for host OUs.
- **ParentId**: Parent OU's Id. Can be null if this is a root OU.
- **Code**: A hierarchical string code that is unique for a tenant.
- **DisplayName**: Shown name of the OU.
The OrganizationUnit entity's primary key (Id) is a **Guid** type and it derives from the [**FullAuditedAggregateRoot**](../Entities.md) class.
#### Organization Tree
Since an OU can have a parent, all OUs of a tenant are in a **tree** structure. There are some rules for this tree;
- There can be more than one root (where the `ParentId` is `null`).
- There is a limit for the first-level children count of an OU (because of the fixed OU Code unit length explained below).
#### OU Code
OU code is automatically generated and maintained by the OrganizationUnit Manager. It's a string that looks something like this:
"**00001.00042.00005**"
This code can be used to easily query the database for all the children of an OU (recursively). There are some rules for this code:
- It must be **unique** for a [tenant](../Multi-Tenancy.md).
- All the children of the same OU have codes that **start with the parent OU's code**.
- It's **fixed length** and based on the level of the OU in the tree, as shown in the sample.
- While the OU code is unique, it can be **changeable** if you move an OU.
- You must reference an OU by Id, not Code.
### OrganizationUnit Manager
The **OrganizationUnitManager** class can be [injected](../Dependency-Injection.md) and used to manage OUs. Common use cases are:
- Create, Update or Delete an OU
- Move an OU in the OU tree.
- Getting information about the OU tree and its items.
#### Multi-Tenancy
The `OrganizationUnitManager` is designed to work for a **single tenant** at a time. It works for the **current tenant** by default.

26
docs/en/Modules/Index.md

@ -1,6 +1,6 @@
# Application Modules
ABP is a **modular application framework** which consists of dozens of **nuget packages**. It also provides a complete infrastructure to build your own application modules which may have entities, services, database integration, APIs, UI components and so on.
ABP is a **modular application framework** which consists of dozens of **NuGet & NPM packages**. It also provides a complete infrastructure to build your own application modules which may have entities, services, database integration, APIs, UI components and so on.
There are **two types of modules.** They don't have any structural difference but categorized by functionality and purpose:
@ -9,24 +9,26 @@ There are **two types of modules.** They don't have any structural difference bu
## Open Source Application Modules
There are some **free and open source** application modules developed and maintained by the ABP community:
There are some **free and open source** application modules developed and maintained as a part of the ABP Framework.
* **Account**: Provides UI for the account management and allows user to login/register to the application.
* [**Account**](Account.md): Provides UI for the account management and allows user to login/register to the application.
* [**Audit Logging**](Audit-Logging.md): Persists audit logs to a database.
* **Background Jobs**: Persist background jobs when using the default background job manager.
* **Blogging**: Used to create fancy blogs. ABP's [own blog](https://blog.abp.io/) already using this module.
* [**Docs**](Docs.md): Used to create technical documentation pages. ABP's [own documentation](https://docs.abp.io) already using this module.
* **Feature Management**: Used to persist and manage the [features](../Features.md).
* [**Background Jobs**](Background-Jobs.md): Persist background jobs when using the default background job manager.
* [**Blogging**](Blogging.md): Used to create fancy blogs. ABP's [own blog](https://blog.abp.io/) already using this module.
* [**Client Simulation**](Client-Simulation.md): A simple web UI to stress test HTTP APIs by simulating concurrent clients.
* [**CMS Kit**](Cms-Kit.md): A set of reusable *Content Management System* features.
* [**Docs**](Docs.md): Used to create technical documentation website. ABP's [own documentation](https://docs.abp.io) already using this module.
* [**Feature Management**](Feature-Management.md): Used to persist and manage the [features](../Features.md).
* **[Identity](Identity.md)**: Manages organization units, roles, users and their permissions, based on the Microsoft Identity library.
* **IdentityServer**: Integrates to IdentityServer4.
* **Permission Management**: Used to persist permissions.
* [**IdentityServer**](IdentityServer.md): Integrates to IdentityServer4.
* [**Permission Management**](Permission-Management.md): Used to persist permissions.
* **[Setting Management](Setting-Management.md)**: Used to persist and manage the [settings](../Settings.md).
* **Tenant Management**: Manages tenants for a [multi-tenant](../Multi-Tenancy.md) application.
* **Users**: Abstract users, so other modules can depend on this module instead of the Identity module.
* [**Tenant Management**](Tenant-Management.md): Manages tenants for a [multi-tenant](../Multi-Tenancy.md) application.
* [**Users**](Users.md): Abstract users, so other modules can depend on this module instead of the Identity module.
* [**Virtual File Explorer**](Virtual-File-Explorer.md): Provided a simple UI to view files in [virtual file system](../Virtual-File-System.md).
See [the GitHub repository](https://github.com/abpframework/abp/tree/master/modules) for source code of all modules.
## Commercial Application Modules
[ABP Commercial](https://commercial.abp.io/) license provides additional pre-built application modules on top of the ABP framework. See the [module list](https://commercial.abp.io/modules) provided by the ABP Commercial.
[ABP Commercial](https://commercial.abp.io/) license provides **additional pre-built application modules** on top of the ABP framework. See the [module list](https://commercial.abp.io/modules) provided by the ABP Commercial.

47
docs/en/Modules/Organization-Units.md

@ -1,47 +0,0 @@
# Organization Unit Management
Organization units (OU) is a part of **Identity Module** and can be used to **hierarchically group users and entities**.
### OrganizationUnit Entity
An OU is represented by the **OrganizationUnit** entity. The fundamental properties of this entity are:
- **TenantId**: Tenant's Id of this OU. Can be null for host OUs.
- **ParentId**: Parent OU's Id. Can be null if this is a root OU.
- **Code**: A hierarchical string code that is unique for a tenant.
- **DisplayName**: Shown name of the OU.
The OrganizationUnit entity's primary key (Id) is a **Guid** type and it derives from the [**FullAuditedAggregateRoot**](../Entities.md) class.
#### Organization Tree
Since an OU can have a parent, all OUs of a tenant are in a **tree** structure. There are some rules for this tree;
- There can be more than one root (where the `ParentId` is `null`).
- There is a limit for the first-level children count of an OU (because of the fixed OU Code unit length explained below).
#### OU Code
OU code is automatically generated and maintained by the OrganizationUnit Manager. It's a string that looks something like this:
"**00001.00042.00005**"
This code can be used to easily query the database for all the children of an OU (recursively). There are some rules for this code:
- It must be **unique** for a [tenant](../Multi-Tenancy.md).
- All the children of the same OU have codes that **start with the parent OU's code**.
- It's **fixed length** and based on the level of the OU in the tree, as shown in the sample.
- While the OU code is unique, it can be **changeable** if you move an OU.
- You must reference an OU by Id, not Code.
### OrganizationUnit Manager
The **OrganizationUnitManager** class can be [injected](../Dependency-Injection.md) and used to manage OUs. Common use cases are:
- Create, Update or Delete an OU
- Move an OU in the OU tree.
- Getting information about the OU tree and its items.
#### Multi-Tenancy
The `OrganizationUnitManager` is designed to work for a **single tenant** at a time. It works for the **current tenant** by default.

3
docs/en/Modules/Users.md

@ -0,0 +1,3 @@
# Users Module
TODO

16
docs/en/MongoDB.md

@ -40,7 +40,7 @@ public class MyDbContext : AbpMongoDbContext
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);
//Customize the configuration for your collections.
}
}
@ -62,7 +62,7 @@ So, most of times you don't need to explicitly configure registration for your e
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);
modelBuilder.Entity<Question>(b =>
{
b.CollectionName = "MyQuestions"; //Sets the collection name
@ -88,7 +88,7 @@ If you have multiple databases in your application, you can configure the connec
[ConnectionStringName("MySecondConnString")]
public class MyDbContext : AbpMongoDbContext
{
}
````
@ -202,7 +202,7 @@ You generally want to derive from the `IRepository` to inherit standard reposito
Example implementation of the `IBookRepository` interface:
```csharp
public class BookRepository :
public class BookRepository :
MongoDbRepository<BookStoreMongoDbContext, Book, Guid>,
IBookRepository
{
@ -242,9 +242,9 @@ context.Services.AddMongoDbContext<BookStoreMongoDbContext>(options =>
This is especially important when you want to **override a base repository method** to customize it. For instance, you may want to override `DeleteAsync` method to delete an entity in a more efficient way:
```csharp
public override async Task DeleteAsync(
Guid id,
bool autoSave = false,
public async override Task DeleteAsync(
Guid id,
bool autoSave = false,
CancellationToken cancellationToken = default)
{
//TODO: Custom implementation of the delete method
@ -381,4 +381,4 @@ context.Services.AddMongoDbContext<OtherMongoDbContext>(options =>
});
```
In this example, `OtherMongoDbContext` implements `IBookStoreMongoDbContext`. This feature allows you to have multiple MongoDbContext (one per module) on development, but single MongoDbContext (implements all interfaces of all MongoDbContexts) on runtime.
In this example, `OtherMongoDbContext` implements `IBookStoreMongoDbContext`. This feature allows you to have multiple MongoDbContext (one per module) on development, but single MongoDbContext (implements all interfaces of all MongoDbContexts) on runtime.

3
docs/en/Multi-Tenancy.md

@ -329,6 +329,7 @@ Configure<AbpTenantResolveOptions>(options =>
`MyCustomTenantResolveContributor` must inherit from the `TenantResolveContributorBase` (or implement the `ITenantResolveContributor`) as shown below:
````csharp
using System.Threading.Tasks;
using Volo.Abp.MultiTenancy;
namespace MultiTenancyDemo.Web
@ -337,7 +338,7 @@ namespace MultiTenancyDemo.Web
{
public override string Name => "Custom";
public override void Resolve(ITenantResolveContext context)
public override Task ResolveAsync(ITenantResolveContext context)
{
//TODO...
}

233
docs/en/PlugIn-Modules.md

@ -0,0 +1,233 @@
# Plug-In Modules
It is possible to load [modules](Module-Development-Basics.md) as plug-ins. That means you may not reference to a module's assembly in your solution, but you can load that module in the application startup just like any other module.
## Basic Usage
`IServiceCollection.AddApplication<T>()` extension method can get options to configure the plug-in sources.
**Example: Load plugins from a folder**
````csharp
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity.PlugIns;
namespace MyPlugInDemo.Web
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<MyPlugInDemoWebModule>(options =>
{
options.PlugInSources.AddFolder(@"D:\Temp\MyPlugIns");
});
}
public void Configure(IApplicationBuilder app)
{
app.InitializeApplication();
}
}
}
````
* This is the `Startup` class of a typical ASP.NET Core application.
* `PlugInSources.AddFolder` gets a folder path and to load assemblies (typically `dll`s) in that folder.
That's all. ABP will discover the modules in the given folder, configure and initialize them just like regular modules.
### Plug-In Sources
`options.PlugInSources` is actually a list of `IPlugInSource` implementations and `AddFolder` is just a shortcut for the following expression:
````csharp
options.PlugInSources.Add(new FolderPlugInSource(@"D:\Temp\MyPlugIns"));
````
> `AddFolder()` only looks for the assembly file in the given folder, but not looks for the sub-folders. You can pass `SearchOption.AllDirectories` as a second parameter to explore plug-ins also from the sub-folders, recursively.
There are two more built-in Plug-In Source implementations:
* `PlugInSources.AddFiles()` gets a list of assembly (typically `dll`) files. This is a shortcut of using `FilePlugInSource` class.
* `PlugInSources.AddTypes()` gets a list of module class types. If you use this, you need to load the assemblies of the modules yourself, but it provides flexibility when needed. This is a shortcut of using `TypePlugInSource` class.
If you need, you can create your own `IPlugInSource` implementation and add to the `options.PlugInSources` just like the others.
## Example: Creating a Simple Plug-In
Create a simple **Class Library Project** in a solution:
![simple-plugin-library](images/simple-plugin-library.png)
You can add ABP Framework packages you need to use in the module. At least, you should add the `Volo.Abp.Core` package to the project:
````
Install-Package Volo.Abp.Core
````
Every [module](Module-Development-Basics.md) must declare a class derived from the `AbpModule`. Here, a simple module class that resolves a service and initializes it on the application startup:
````csharp
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Modularity;
namespace MyPlugIn
{
public class MyPlungInModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var myService = context.ServiceProvider
.GetRequiredService<MyService>();
myService.Initialize();
}
}
}
````
`MyService` can be any class registered to [Dependency Injection](Dependency-Injection.md) system, as show below:
````csharp
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
namespace MyPlugIn
{
public class MyService : ITransientDependency
{
private readonly ILogger<MyService> _logger;
public MyService(ILogger<MyService> logger)
{
_logger = logger;
}
public void Initialize()
{
_logger.LogInformation("MyService has been initialized");
}
}
}
````
Build the project, open the build folder, find the `MyPlugIn.dll`:
![simple-plug-in-dll-file](images/simple-plug-in-dll-file.png)
Copy `MyPlugIn.dll` into the plug-in folder (`D:\Temp\MyPlugIns` for this example).
If you have configured the main application like described above (see Basic Usage section), you should see the `MyService has been initialized` log in the application startup.
## Example: Creating a Plug-In With Razor Pages
Creating plug-ins with views inside requires a bit more attention.
> This example assumes you've [created a new web application](https://abp.io/get-started) using the application startup template and MVC / Razor Pages UI.
Create a new **Class Library** project in a solution:
![simple-razor-plugin](images/simple-razor-plugin.png)
Edit the `.csproj` file content:
````xml
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared" Version="4.0.1" />
</ItemGroup>
</Project>
````
* Changed `Sdk` to `Microsoft.NET.Sdk.Web`.
* Added `OutputType` and `IsPackable` properties.
* Added `Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared` NuGet package.
> Depending on [Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared) package is not required. You can reference to a more base package like [Volo.Abp.AspNetCore.Mvc](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc/). However, if you will build a UI page/component, it is suggested to reference to the [Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared) package since it is the most high-level package without depending on a particular [theme](UI/AspNetCore/Theming.md). If there is no problem to depend on a particular theme, you can directly reference to the theme's package to be able to use the theme-specific features in your plug-in.
Then create your module class in the plug-in:
````csharp
using System.IO;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.Modularity;
namespace MyMvcUIPlugIn
{
[DependsOn(typeof(AbpAspNetCoreMvcUiThemeSharedModule))]
public class MyMvcUIPlugInModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
//Add plugin assembly
mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(typeof(MyMvcUIPlugInModule).Assembly));
//Add views assembly
var viewDllPath = Path.Combine(Path.GetDirectoryName(typeof(MyMvcUIPlugInModule).Assembly.Location), "MyMvcUIPlugIn.Views.dll");
var viewAssembly = new CompiledRazorAssemblyPart(Assembly.LoadFrom(viewDllPath));
mvcBuilder.PartManager.ApplicationParts.Add(viewAssembly);
});
}
}
}
````
* Depending on the `AbpAspNetCoreMvcUiThemeSharedModule` since we added the related NuGet package.
* Adding the plug-in's assembly to the `PartManager` of ASP.NET Core MVC. This is required by ASP.NET Core. Otherwise, your controllers inside the plug-in doesn't work.
* Adding the plug-in's views assembly to the `PartManager` of ASP.NET Core MVC. This is required by ASP.NET Core. Otherwise, your views inside the plug-in doesn't work.
You can now add a razor page, like `MyPlugInPage.cshtml` inside the `Pages` folder:
````html
@page
@model MyMvcUIPlugIn.Pages.MyPlugInPage
<h1>Welcome to my plug-in page</h1>
<p>This page is located inside a plug-in module! :)</p>
````
Now, you can build the plug-in project. It will produce the following output:
![simple-razor-plug-in-dll-file](images/simple-razor-plug-in-dll-file.png)
Copy the `MyMvcUIPlugIn.dll` and `MyMvcUIPlugIn.Views.dll` into the plug-in folder (`D:\Temp\MyPlugIns` for this example).
If you have configured the main application like described above (see Basic Usage section), you should be able to visit the `/MyPlugInPage` URL when your application:
![simple-plugin-output](images/simple-plugin-output.png)
## Discussions
In real world, your plug-in may have some external dependencies. Also, your application might be designed to support plug-ins. All these are your own system requirements. What ABP does is just loading modules on the application startup. What you do inside that modules is up to you.
However, we can provide a few suggestions for some common cases.
### Library Dependencies
For package/dll dependencies, you can copy the related dlls to the plug-in folder. ABP automatically loads all assemblies in the folder and your plug-in will work as expected.
> See [Microsoft's documentation](https://docs.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support#plugin-with-library-dependencies) for some additional explanations for that case.
### Database Schema
If your module uses a relational database and [Entity Framework Core](Entity-Framework-Core.md), it will need to have its tables available in the database. There are different ways to ensure the tables have been created when an application uses the plug-in. Some examples;
1. The Plugin may check if the database tables does exists and create the tables on the application startup or migrate them if the plug-in has been updated and requires some schema changes. You can use EF Core's migration API to do that.
2. You can improve the `DbMigrator` application to find migrations of the plug-ins and execute them.
There may be other solutions. For example, if your DB admin doesn't allow you to change the database schema in the application code, you may need to manually send a SQL file to the database admin to apply it to the database.

8
docs/en/Repositories.md

@ -79,6 +79,14 @@ If your entity does not have an Id primary key (it may have a composite primary
> `IRepository<TEntity>` has a few missing methods those normally works with the `Id` property of an entity. Because of the entity has no `Id` property in that case, these methods are not available. One example is the `Get` method that gets an id and returns the entity with given id. However, you can still use `IQueryable<TEntity>` features to query entities by standard LINQ methods.
### Soft / Hard Delete
`DeleteAsync` method of the repository doesn't delete the entity if the entity is a **soft-delete** entity (that implements `ISoftDelete`). Soft-delete entities are marked as "deleted" in the database. Data Filter system ensures that the soft deleted entities are not retrieved from database normally.
If your entity is a soft-delete entity, you can use the `HardDeleteAsync` method to really delete the entity from database in case of you need it.
See the [Data Filtering](Data-Filtering.md) documentation for more about soft-delete.
## Custom Repositories
Default generic repositories will be sufficient for most cases. However, you may need to create a custom repository class for your entity.

11
docs/en/Road-Map.md

@ -1,17 +1,12 @@
# ABP Framework Road Map
You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones) for a detailed road map.
While we will **continue to add other exciting features**, we will work on the following major items in the **middle term**:
* **Blazor UI** for the framework and all the pre-built modules (in progress).
* **.NET 5.0**! As Microsoft has announced that the .NET 5.0 will be released in November 2020, we will prepare for this change before and move to the .NET 5.0 just after Microsoft releases it. We hope a smooth transition.
Beside this middle term goals, there are many features in the [backlog](https://github.com/abpframework/abp/milestone/2). Here, a list of some major items in the backlog;
You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones) for a detailed road map. Here, a list of some major items in the backlog;
* [#2882](https://github.com/abpframework/abp/issues/2882) / Providing a gRPC integration infrastructure (while it is [already possible](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) to create or consume gRPC endpoints for your application, we plan to create endpoints for the [standard application modules](https://docs.abp.io/en/abp/latest/Modules/Index))
* [#236](https://github.com/abpframework/abp/issues/236) Resource based authorization system
* [#6132](https://github.com/abpframework/abp/issues/6132) A New Theme alternative to the Basic Theme
* [#1754](https://github.com/abpframework/abp/issues/1754) / Multi-lingual entities
* [#497](https://github.com/abpframework/abp/issues/497) API Versioning system finalize & document
* [#633](https://github.com/abpframework/abp/issues/633) / Realtime notification system
* [#57](https://github.com/abpframework/abp/issues/57) / Built-in CQRS infrastructure
* [#336](https://github.com/abpframework/abp/issues/336) / Health Check abstraction

6
docs/en/Specifications.md

@ -2,9 +2,13 @@
Specification Pattern is used to define **named, reusable, combinable and testable filters** for entities and other business objects.
> A Specification is a part of the Domain Layer.
## Installation
If you haven't installed yet, install the [Volo.Abp.Specifications](https://abp.io/package-detail/Volo.Abp.Specifications) package to your project. You can use the [ABP CLI](CLI.md) *add-package* command in a command line terminal when the current folder is the root folder of your project (`.csproj`):
> This package is **already installed** when you use the startup templates. So, most of the times you don't need to manually install it.
Install the [Volo.Abp.Specifications](https://abp.io/package-detail/Volo.Abp.Specifications) package to your project. You can use the [ABP CLI](CLI.md) *add-package* command in a command line terminal when the current folder is the root folder of your project (`.csproj`):
````bash
abp add-package Volo.Abp.Specifications

2
docs/en/Startup-Templates/Application.md

@ -97,7 +97,7 @@ A `BookType` enum and a `BookConsts` class (which may have some constant fields
#### .Domain Project
This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects.
This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value objects](../Value-Objects.md), [repository interfaces](../Repositories.md) and other domain objects.
A `Book` entity, a `BookManager` domain service and an `IBookRepository` interface are good candidates for this project.

1
docs/en/Startup-Templates/Index.md

@ -5,3 +5,4 @@ While you can start with an empty project and add needed packages manually, star
* [**app**](Application.md): Application template.
* [**module**](Module.md): Module/service template.
* [**console**](Console.md): Console template.
* [**WPF**](WPF.md): WPF template.

27
docs/en/Startup-Templates/WPF.md

@ -0,0 +1,27 @@
# WPF Application Startup Template
This template is used to create a minimalist WPF application project.
## How to Start With?
First, install the [ABP CLI](../CLI.md) if you haven't installed before:
````bash
dotnet tool install -g Volo.Abp.Cli
````
Then use the `abp new` command in an empty folder to create a new solution:
````bash
abp new Acme.MyWpfApp -t wpf
````
`Acme.MyWpfApp` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming.
## Solution Structure
After you use the above command to create a solution, you will have a solution like shown below:
![basic-wpf-application-solution](../images/basic-wpf-application-solution.png)
* `HelloWorldService` is a sample service that implements the `ITransientDependency` interface to register this service to the [dependency injection](../Dependency-Injection.md) system.

3
docs/en/Swagger.md

@ -0,0 +1,3 @@
# Swagger UI Integration
TODO

51
docs/en/Tutorials/Part-10.md

@ -10,7 +10,7 @@
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Value}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -50,7 +50,7 @@ public Guid AuthorId { get; set; }
{{if DB=="EF"}}
> In this tutorial, we preferred to not add a **navigation property** to the `Author` entity from the `Book` class (like `public Author Author { get; set; }`). This is due to follow the DDD best practices (rule: refer to other aggregates only by id). However, you can add such a navigation property and configure it for the EF Core. In this way, you don't need to write join queries while getting books with their authors (just like we will done below) which makes your application code simpler.
> In this tutorial, we preferred to not add a **navigation property** to the `Author` entity from the `Book` class (like `public Author Author { get; set; }`). This is due to follow the DDD best practices (rule: refer to other aggregates only by id). However, you can add such a navigation property and configure it for the EF Core. In this way, you don't need to write join queries while getting books with their authors (like we will done below) which makes your application code simpler.
{{end}}
@ -78,7 +78,7 @@ builder.Entity<Book>(b =>
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
// ADD THE MAPPING FOR THE RELATION
b.HasOne<Author>().WithMany().HasForeignKey(x => x.AuthorId).IsRequired();
});
@ -384,8 +384,8 @@ namespace Acme.BookStore.Books
return bookDto;
}
public override async Task<PagedResultDto<BookDto>>
GetListAsync(PagedAndSortedResultRequestDto input)
public override async Task<PagedResultDto<BookDto>> GetListAsync(
PagedAndSortedResultRequestDto input)
{
await CheckGetListPolicyAsync();
@ -485,7 +485,7 @@ namespace Acme.BookStore.Books
DeletePolicyName = BookStorePermissions.Books.Create;
}
public override async Task<BookDto> GetAsync(Guid id)
public async override Task<BookDto> GetAsync(Guid id)
{
await CheckGetPolicyAsync();
@ -498,7 +498,7 @@ namespace Acme.BookStore.Books
return bookDto;
}
public override async Task<PagedResultDto<BookDto>>
public async override Task<PagedResultDto<BookDto>>
GetListAsync(PagedAndSortedResultRequestDto input)
{
await CheckGetListPolicyAsync();
@ -524,7 +524,7 @@ namespace Acme.BookStore.Books
var authorDictionary = await GetAuthorDictionaryAsync(books);
//Set AuthorName for the DTOs
bookDtos.ForEach(bookDto => bookDto.AuthorName =
bookDtos.ForEach(bookDto => bookDto.AuthorName =
authorDictionary[bookDto.AuthorId].Name);
//Get the total count with another query (required for the paging)
@ -622,7 +622,7 @@ namespace Acme.BookStore.Books
result.Items.ShouldContain(b => b.Name == "1984" &&
b.AuthorName == "George Orwell");
}
[Fact]
public async Task Should_Create_A_Valid_Book()
{
@ -645,7 +645,7 @@ namespace Acme.BookStore.Books
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}
[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
@ -1093,36 +1093,37 @@ Add the following field to the `@code` section of the `Books.razor` file:
IReadOnlyList<AuthorLookupDto> authorList = Array.Empty<AuthorLookupDto>();
````
And fill it in the `OnInitializedAsync` method, by adding the following code to the end of the method:
Override the `OnInitializedAsync` method and adding the following code:
````csharp
authorList = (await AppService.GetAuthorLookupAsync()).Items;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
authorList = (await AppService.GetAuthorLookupAsync()).Items;
}
````
* It is essential to call the `base.OnInitializedAsync()` since `AbpCrudPageBase` has some initialization code to be executed.
The final `@code` block should be the following:
````csharp
@code
{
bool canCreateBook;
bool canEditBook;
bool canDeleteBook;
//ADDED A NEW FIELD
IReadOnlyList<AuthorLookupDto> authorList = Array.Empty<AuthorLookupDto>();
public Books() // Constructor
{
CreatePolicyName = BookStorePermissions.Books.Create;
UpdatePolicyName = BookStorePermissions.Books.Edit;
DeletePolicyName = BookStorePermissions.Books.Delete;
}
//GET AUTHORS ON INITIALIZATION
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
canCreateBook = await
AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create);
canEditBook = await
AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Edit);
canDeleteBook = await
AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Delete);
//GET AUTHORS
authorList = (await AppService.GetAuthorLookupAsync()).Items;
}
}

5
docs/en/Tutorials/Part-2.md

@ -590,7 +590,6 @@ Open the `Books.razor` and replace the content as the following:
````xml
@page "/books"
@using Volo.Abp.Application.Dtos
@using Volo.Abp.BlazoriseUI
@using Acme.BookStore.Books
@using Acme.BookStore.Localization
@using Microsoft.Extensions.Localization
@ -620,14 +619,14 @@ Open the `Books.razor` and replace the content as the following:
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="BookDto"
Field="@nameof(BookDto.PublishDate)"
Field="@nameof(BookDto.PublishDate)"
Caption="@L["PublishDate"]">
<DisplayTemplate>
@context.PublishDate.ToShortDateString()
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="BookDto"
Field="@nameof(BookDto.Price)"
Field="@nameof(BookDto.Price)"
Caption="@L["Price"]">
</DataGridColumn>
<DataGridColumn TItem="BookDto"

452
docs/en/Tutorials/Part-3.md

@ -1171,15 +1171,13 @@ Open the `Books.razor` and replace the `<CardHeader>` section with the following
````xml
<CardHeader>
<Row>
<Column ColumnSize="ColumnSize.Is6">
<Row Class="justify-content-between">
<Column ColumnSize="ColumnSize.IsAuto">
<h2>@L["Books"]</h2>
</Column>
<Column ColumnSize="ColumnSize.Is6">
<Paragraph Alignment="TextAlignment.Right">
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">@L["NewBook"]</Button>
</Paragraph>
<Column ColumnSize="ColumnSize.IsAuto">
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">@L["NewBook"]</Button>
</Column>
</Row>
</CardHeader>
@ -1196,48 +1194,67 @@ Now, we can add a modal that will be opened when we click to the button.
Open the `Books.razor` and add the following code to the end of the page:
````xml
<Modal @ref="CreateModal">
<Modal @ref="@CreateModal">
<ModalBackdrop />
<ModalContent IsCentered="true">
<ModalHeader>
<ModalTitle>@L["NewBook"]</ModalTitle>
<CloseButton Clicked="CloseCreateModalAsync" />
</ModalHeader>
<ModalBody>
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-text="NewEntity.Name" />
</Field>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@NewEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType)bookTypeValue)">
@L[$"Enum:BookType:{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="NewEntity.PublishDate" />
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="NewEntity.Price" />
</Field>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseCreateModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Clicked="CreateEntityAsync">@L["Save"]</Button>
</ModalFooter>
<Form>
<ModalHeader>
<ModalTitle>@L["NewBook"]</ModalTitle>
<CloseButton Clicked="CloseCreateModalAsync"/>
</ModalHeader>
<ModalBody>
<Validations @ref="@CreateValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-Text="@NewEntity.Name">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@NewEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType) bookTypeValue)">
@L[$"Enum:BookType:{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="NewEntity.PublishDate"/>
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="NewEntity.Price"/>
</Field>
</Validations>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseCreateModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Type="@ButtonType.Submit"
PreventDefaultOnSubmit="true"
Clicked="CreateEntityAsync">@L["Save"]</Button>
</ModalFooter>
</Form>
</ModalContent>
</Modal>
````
This code requires a service; Inject the `AbpBlazorMessageLocalizerHelper<T>` at the top of the file, just before the `@inherits...` line:
````csharp
@inject AbpBlazorMessageLocalizerHelper<BookStoreResource> LH
````
* The form implements validation and the `AbpBlazorMessageLocalizerHelper` is used to simply localize the validation messages.
* `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` method are defined by the base class. See the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components.
That's all. Run the application and try to add a new book:
@ -1250,78 +1267,81 @@ Editing a books is similar to the creating a new book.
### Actions Dropdown
Open the `Books.razor` and add the following `DataGridColumn` section inside the `DataGridColumns` as the first item:
Open the `Books.razor` and add the following `DataGridEntityActionsColumn` section inside the `DataGridColumns` as the first item:
````xml
<DataGridColumn Width="150px"
TItem="BookDto"
Field="@nameof(BookDto.Id)"
Sortable="false"
Caption="@L["Actions"]">
<DataGridEntityActionsColumn TItem="BookDto" @ref="@EntityActionsColumn">
<DisplayTemplate>
<Dropdown>
<DropdownToggle Color="Color.Primary">
@L["Actions"]
</DropdownToggle>
<DropdownMenu>
<DropdownItem Clicked="() => OpenEditModalAsync(context.Id)">
@L["Edit"]
</DropdownItem>
</DropdownMenu>
</Dropdown>
<EntityActions TItem="BookDto" EntityActionsColumn="@EntityActionsColumn">
<EntityAction TItem="BookDto"
Text="@L["Edit"]"
Clicked="() => OpenEditModalAsync(context)" />
</EntityActions>
</DisplayTemplate>
</DataGridColumn>
</DataGridEntityActionsColumn>
````
* `OpenEditModalAsync` is defined in the base class which takes the `Id` of the entity (book) to edit.
* `OpenEditModalAsync` is defined in the base class which takes the entity (book) to edit.
This adds an "Actions" dropdown to all the books inside the `DataGrid` with an `Edit` action:
`DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. `DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it:
![blazor-edit-book-action](images/blazor-edit-book-action.png)
![blazor-edit-book-action](images/blazor-edit-book-action-2.png)
### Edit Modal
We can now define a modal to edit the book. Add the following code to the end of the `Books.razor` page:
````xml
<Modal @ref="EditModal">
<Modal @ref="@EditModal">
<ModalBackdrop />
<ModalContent IsCentered="true">
<ModalHeader>
<ModalTitle>@EditingEntity.Name</ModalTitle>
<CloseButton Clicked="CloseEditModalAsync" />
</ModalHeader>
<ModalBody>
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-text="EditingEntity.Name" />
</Field>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@EditingEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType)bookTypeValue)">
@L[$"Enum:BookType:{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="EditingEntity.PublishDate" />
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="EditingEntity.Price" />
</Field>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseEditModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Clicked="UpdateEntityAsync">@L["Save"]</Button>
</ModalFooter>
<Form>
<ModalHeader>
<ModalTitle>@EditingEntity.Name</ModalTitle>
<CloseButton Clicked="CloseEditModalAsync"/>
</ModalHeader>
<ModalBody>
<Validations @ref="@EditValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-Text="@EditingEntity.Name">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@EditingEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType) bookTypeValue)">
@L[$"Enum:BookType:{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="EditingEntity.PublishDate"/>
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="EditingEntity.Price"/>
</Field>
</Validations>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseEditModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Type="@ButtonType.Submit"
PreventDefaultOnSubmit="true"
Clicked="UpdateEntityAsync">@L["Save"]</Button>
</ModalFooter>
</Form>
</ModalContent>
</Modal>
````
@ -1356,17 +1376,26 @@ You can now run the application and try to edit a book.
![blazor-edit-book-modal](images/blazor-edit-book-modal.png)
> Tip: Try to leave the *Name* field empty and submit the form to show the validation error message.
## Deleting a Book
Open the `Books.razor` page and add the following `DropdownItem` under the "Edit" action inside the "Actions" `DropdownMenu`:
Open the `Books.razor` page and add the following `EntityAction` under the "Edit" action inside the `EntityActions`:
````xml
<DropdownItem Clicked="() => DeleteEntityAsync(context)">
@L["Delete"]
</DropdownItem>
<EntityAction TItem="BookDto"
Text="@L["Delete"]"
Clicked="() => DeleteEntityAsync(context)"
ConfirmationMessage="() => GetDeleteConfirmationMessage(context)" />
````
* `DeleteEntityAsync` is defined in the base class.
* `DeleteEntityAsync` is defined in the base class that deletes the entity by performing a call to the server.
* `ConfirmationMessage` is a callback to show a confirmation message before executing the action.
* `GetDeleteConfirmationMessage` is defined in the base class. You can override this method (or pass another value to the `ConfirmationMessage` parameter) to customize the localization message.
The "Actions" button becomes a dropdown since it has two actions now:
![blazor-edit-book-action](images/blazor-delete-book-action.png)
Run the application and try to delete a book.
@ -1377,26 +1406,22 @@ Here the complete code to create the book management CRUD page, that has been de
````xml
@page "/books"
@using Volo.Abp.Application.Dtos
@using Volo.Abp.BlazoriseUI
@using Acme.BookStore.Books
@using Acme.BookStore.Localization
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<BookStoreResource> L
@inject AbpBlazorMessageLocalizerHelper<BookStoreResource> LH
@inherits AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
<Card>
<CardHeader>
<Row>
<Column ColumnSize="ColumnSize.Is6">
<Row Class="justify-content-between">
<Column ColumnSize="ColumnSize.IsAuto">
<h2>@L["Books"]</h2>
</Column>
<Column ColumnSize="ColumnSize.Is6">
<Paragraph Alignment="TextAlignment.Right">
<Column ColumnSize="ColumnSize.IsAuto">
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">
@L["NewBook"]
</Button>
</Paragraph>
Clicked="OpenCreateModalAsync">@L["NewBook"]</Button>
</Column>
</Row>
</CardHeader>
@ -1404,31 +1429,24 @@ Here the complete code to create the book management CRUD page, that has been de
<DataGrid TItem="BookDto"
Data="Entities"
ReadData="OnDataGridReadAsync"
CurrentPage="CurrentPage"
TotalItems="TotalCount"
ShowPager="true"
PageSize="PageSize">
<DataGridColumns>
<DataGridColumn Width="150px"
TItem="BookDto"
Field="@nameof(BookDto.Id)"
Sortable="false"
Caption="@L["Actions"]">
<DataGridEntityActionsColumn TItem="BookDto" @ref="@EntityActionsColumn">
<DisplayTemplate>
<Dropdown>
<DropdownToggle Color="Color.Primary">
@L["Actions"]
</DropdownToggle>
<DropdownMenu>
<DropdownItem Clicked="() => OpenEditModalAsync(context.Id)">
@L["Edit"]
</DropdownItem>
<DropdownItem Clicked="() => DeleteEntityAsync(context)">
@L["Delete"]
</DropdownItem>
</DropdownMenu>
</Dropdown>
<EntityActions TItem="BookDto" EntityActionsColumn="@EntityActionsColumn">
<EntityAction TItem="BookDto"
Text="@L["Edit"]"
Clicked="() => OpenEditModalAsync(context)" />
<EntityAction TItem="BookDto"
Text="@L["Delete"]"
Clicked="() => DeleteEntityAsync(context)"
ConfirmationMessage="()=>GetDeleteConfirmationMessage(context)" />
</EntityActions>
</DisplayTemplate>
</DataGridColumn>
</DataGridEntityActionsColumn>
<DataGridColumn TItem="BookDto"
Field="@nameof(BookDto.Name)"
Caption="@L["Name"]"></DataGridColumn>
@ -1436,7 +1454,7 @@ Here the complete code to create the book management CRUD page, that has been de
Field="@nameof(BookDto.Type)"
Caption="@L["Type"]">
<DisplayTemplate>
@L[$"Enum:BookType:{(int)context.Type}"]
@L[$"Enum:BookType:{(int) context.Type}"]
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="BookDto"
@ -1462,85 +1480,109 @@ Here the complete code to create the book management CRUD page, that has been de
</CardBody>
</Card>
<Modal @ref="CreateModal">
<Modal @ref="@CreateModal">
<ModalBackdrop />
<ModalContent IsCentered="true">
<ModalHeader>
<ModalTitle>@L["NewBook"]</ModalTitle>
<CloseButton Clicked="CloseCreateModalAsync" />
</ModalHeader>
<ModalBody>
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-text="NewEntity.Name" />
</Field>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@NewEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType)bookTypeValue)">
@L[$"Enum:BookType:{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="NewEntity.PublishDate" />
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="NewEntity.Price" />
</Field>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseCreateModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Clicked="CreateEntityAsync">@L["Save"]</Button>
</ModalFooter>
<Form>
<ModalHeader>
<ModalTitle>@L["NewBook"]</ModalTitle>
<CloseButton Clicked="CloseCreateModalAsync"/>
</ModalHeader>
<ModalBody>
<Validations @ref="@CreateValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-Text="@NewEntity.Name">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@NewEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType) bookTypeValue)">
@L[$"Enum:BookType:{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="NewEntity.PublishDate"/>
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="NewEntity.Price"/>
</Field>
</Validations>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseCreateModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Type="@ButtonType.Submit"
PreventDefaultOnSubmit="true"
Clicked="CreateEntityAsync">@L["Save"]</Button>
</ModalFooter>
</Form>
</ModalContent>
</Modal>
<Modal @ref="EditModal">
<Modal @ref="@EditModal">
<ModalBackdrop />
<ModalContent IsCentered="true">
<ModalHeader>
<ModalTitle>@EditingEntity.Name</ModalTitle>
<CloseButton Clicked="CloseEditModalAsync" />
</ModalHeader>
<ModalBody>
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-text="EditingEntity.Name" />
</Field>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@EditingEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType)bookTypeValue)">
@L[$"Enum:BookType:{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="EditingEntity.PublishDate" />
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="EditingEntity.Price" />
</Field>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseEditModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Clicked="UpdateEntityAsync">@L["Save"]</Button>
</ModalFooter>
<Form>
<ModalHeader>
<ModalTitle>@EditingEntity.Name</ModalTitle>
<CloseButton Clicked="CloseEditModalAsync"/>
</ModalHeader>
<ModalBody>
<Validations @ref="@EditValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-Text="@EditingEntity.Name">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@EditingEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType) bookTypeValue)">
@L[$"Enum:BookType:{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="EditingEntity.PublishDate"/>
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="EditingEntity.Price"/>
</Field>
</Validations>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseEditModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Type="@ButtonType.Submit"
PreventDefaultOnSubmit="true"
Clicked="UpdateEntityAsync">@L["Save"]</Button>
</ModalFooter>
</Form>
</ModalContent>
</Modal>
````

6
docs/en/Tutorials/Part-4.md

@ -53,7 +53,7 @@ This part covers the **server side** tests. There are several test projects in t
Each project is used to test the related project. Test projects use the following libraries for testing:
* [Xunit](https://xunit.github.io/) as the main test framework.
* [Shoudly](http://shouldly.readthedocs.io/en/latest/) as the assertion library.
* [Shoudly](https://github.com/shouldly/shouldly) as the assertion library.
* [NSubstitute](http://nsubstitute.github.io/) as the mocking library.
{{if DB=="EF"}}
@ -126,7 +126,7 @@ public async Task Should_Create_A_Valid_Book()
{
Name = "New test book 42",
Price = 10,
PublishDate = System.DateTime.Now,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
@ -208,7 +208,7 @@ namespace Acme.BookStore.Books
{
Name = "New test book 42",
Price = 10,
PublishDate = System.DateTime.Now,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);

138
docs/en/Tutorials/Part-5.md

@ -70,7 +70,7 @@ namespace Acme.BookStore.Permissions
}
````
This is a hierarchical way of defining permission names. For example, "create book" permission name was defined as `BookStore.Books.Create`.
This is a hierarchical way of defining permission names. For example, "create book" permission name was defined as `BookStore.Books.Create`. ABP doesn't force you to a structure, but we find this way useful.
### Permission Definitions
@ -423,13 +423,13 @@ Open the `/src/app/book/book.component.html` file and replace the create button
````html
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Create" id="create" class="btn btn-primary" type="button" (click)="createBook()">
<button *abpPermission="'BookStore.Books.Create'" id="create" class="btn btn-primary" type="button" (click)="createBook()">
<i class="fa fa-plus mr-1"></i>
<span>{%{{{ '::NewBook' | abpLocalization }}}%}</span>
</button>
````
* Just added `abpPermission="BookStore.Books.Create"` that hides the button if the current user has no permission.
* Just added `*abpPermission="'BookStore.Books.Create'"` that hides the button if the current user has no permission.
### Hide the Edit and Delete Actions
@ -443,18 +443,18 @@ Open the `/src/app/book/book.component.html` file and replace the edit and delet
````html
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Edit" ngbDropdownItem (click)="editBook(row.id)">
<button *abpPermission="'BookStore.Books.Edit'" ngbDropdownItem (click)="editBook(row.id)">
{%{{{ '::Edit' | abpLocalization }}}%}
</button>
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Delete" ngbDropdownItem (click)="delete(row.id)">
<button *abpPermission="'BookStore.Books.Delete'" ngbDropdownItem (click)="delete(row.id)">
{%{{{ '::Delete' | abpLocalization }}}%}
</button>
````
* Added `abpPermission="BookStore.Books.Edit"` that hides the edit action if the current user has no editing permission.
* Added `abpPermission="BookStore.Books.Delete"` that hides the delete action if the current user has no delete permission.
* Added `*abpPermission="'BookStore.Books.Edit'"` that hides the edit action if the current user has no editing permission.
* Added `*abpPermission="'BookStore.Books.Delete'"` that hides the delete action if the current user has no delete permission.
{{else if UI == "Blazor"}}
@ -476,29 +476,29 @@ Adding this attribute prevents to enter this page if the current hasn't logged i
The book management page has a *New Book* button and *Edit* and *Delete* actions for each book. We should hide these buttons/actions if the current user has not granted for the related permissions.
#### Get the Permissions On Initialization
The base `AbpCrudPageBase` class already has the necessary functionality for these kind of operations.
#### Set the Policy (Permission) Names
Add the following code block to the end of the `Books.razor` file:
````csharp
@code
{
bool canCreateBook;
bool canEditBook;
bool canDeleteBook;
protected override async Task OnInitializedAsync()
public Books() // Constructor
{
await base.OnInitializedAsync();
canCreateBook =await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create);
canEditBook = await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Edit);
canDeleteBook = await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Delete);
CreatePolicyName = BookStorePermissions.Books.Create;
UpdatePolicyName = BookStorePermissions.Books.Edit;
DeletePolicyName = BookStorePermissions.Books.Delete;
}
}
````
We will use these `bool` fields to check the permissions. `AuthorizationService` comes from the base class as an injected property.
The base `AbpCrudPageBase` class automatically checks these permissions on the related operations. It also defines the given properties for us if we need to check them manually:
* `HasCreatePermission`: True, if the current user has permission to create the entity.
* `HasUpdatePermission`: True, if the current user has permission to edit/update the entity.
* `HasDeletePermission`: True, if the current user has permission to delete the entity.
> **Blazor Tip**: While adding the C# code into a `@code` block is fine for small code parts, it is suggested to use the code behind approach to develop a more maintainable code base when the code block becomes longer. We will use this approach for the authors part.
@ -507,32 +507,31 @@ We will use these `bool` fields to check the permissions. `AuthorizationService`
Wrap the *New Book* button by an `if` block as shown below:
````xml
@if (canCreateBook)
@if (HasCreatePermission)
{
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">
@L["NewBook"]
</Button>
Clicked="OpenCreateModalAsync">@L["NewBook"]</Button>
}
````
#### Hide the Edit/Delete Actions
As similar to the *New Book* button, we can use `if` blocks to conditionally show/hide the *Edit* and *Delete* actions:
`EntityAction` component defines `RequiredPolicy` attribute (parameter) to conditionally show the action based on the user permissions.
Update the `EntityActions` section as shown below:
````xml
@if (canEditBook)
{
<DropdownItem Clicked="() => OpenEditModalAsync(context.Id)">
@L["Edit"]
</DropdownItem>
}
@if (canDeleteBook)
{
<DropdownItem Clicked="() => DeleteEntityAsync(context)">
@L["Delete"]
</DropdownItem>
}
<EntityActions TItem="BookDto" EntityActionsColumn="@EntityActionsColumn">
<EntityAction TItem="BookDto"
Text="@L["Edit"]"
RequiredPolicy="@UpdatePolicyName"
Clicked="() => OpenEditModalAsync(context)" />
<EntityAction TItem="BookDto"
Text="@L["Delete"]"
RequiredPolicy="@DeletePolicyName"
Clicked="() => DeleteEntityAsync(context)"
ConfirmationMessage="()=>GetDeleteConfirmationMessage(context)" />
</EntityActions>
````
#### About the Permission Caching
@ -587,54 +586,39 @@ if (await context.IsGrantedAsync(BookStorePermissions.Books.Default))
}
````
You also need to add `async` keyword to the `ConfigureMenuAsync` method and re-arrange the return values. The final `BookStoreMenuContributor` class should be the following:
You also need to add `async` keyword to the `ConfigureMenuAsync` method and re-arrange the return value. The final `ConfigureMainMenuAsync` method should be the following:
````csharp
using System.Threading.Tasks;
using Acme.BookStore.Localization;
using Acme.BookStore.Permissions;
using Volo.Abp.UI.Navigation;
namespace Acme.BookStore.Blazor
private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
{
public class BookStoreMenuContributor : IMenuContributor
{
public async Task ConfigureMenuAsync(MenuConfigurationContext context)
{
if(context.Menu.DisplayName != StandardMenus.Main)
{
return;
}
var l = context.GetLocalizer<BookStoreResource>();
var l = context.GetLocalizer<BookStoreResource>();
context.Menu.Items.Insert(
0,
new ApplicationMenuItem(
"BookStore.Home",
l["Menu:Home"],
"/",
icon: "fas fa-home"
)
);
context.Menu.Items.Insert(
0,
new ApplicationMenuItem(
"BookStore.Home",
l["Menu:Home"],
"/",
icon: "fas fa-home"
)
);
var bookStoreMenu = new ApplicationMenuItem(
"BooksStore",
l["Menu:BookStore"],
icon: "fa fa-book"
);
var bookStoreMenu = new ApplicationMenuItem(
"BooksStore",
l["Menu:BookStore"],
icon: "fa fa-book"
);
context.Menu.AddItem(bookStoreMenu);
context.Menu.AddItem(bookStoreMenu);
if (await context.IsGrantedAsync(BookStorePermissions.Books.Default))
{
bookStoreMenu.AddItem(new ApplicationMenuItem(
"BooksStore.Books",
l["Menu:Books"],
url: "/books"
));
}
}
//CHECK the PERMISSION
if (await context.IsGrantedAsync(BookStorePermissions.Books.Default))
{
bookStoreMenu.AddItem(new ApplicationMenuItem(
"BooksStore.Books",
l["Menu:Books"],
url: "/books"
));
}
}
````

17
docs/en/Tutorials/Part-9.md

@ -843,15 +843,8 @@ Create a new Razor Component Page, `/Pages/Authors.razor`, in the `Acme.BookStor
````xml
@page "/authors"
@using Acme.BookStore.Authors
@using Acme.BookStore.Localization
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Extensions.Localization
@using Volo.Abp.ObjectMapping
@inherits BookStoreComponentBase
@inject IAuthorAppService AuthorAppService
@inject IStringLocalizer<BookStoreResource> L
@inject IAuthorizationService AuthorizationService
@inject IUiMessageService UiMessageService
@inject IObjectMapper ObjectMapper
<Card>
<CardHeader>
<Row>
@ -1048,10 +1041,10 @@ namespace Acme.BookStore.Blazor.Pages
{
CanCreateAuthor = await AuthorizationService
.IsGrantedAsync(BookStorePermissions.Authors.Create);
CanEditAuthor = await AuthorizationService
.IsGrantedAsync(BookStorePermissions.Authors.Edit);
CanDeleteAuthor = await AuthorizationService
.IsGrantedAsync(BookStorePermissions.Authors.Delete);
}
@ -1105,7 +1098,7 @@ namespace Acme.BookStore.Blazor.Pages
private async Task DeleteAuthorAsync(AuthorDto author)
{
var confirmMessage = L["AuthorDeletionConfirmationMessage", author.Name];
if (!await UiMessageService.ConfirmAsync(confirmMessage))
if (!await Message.Confirm(confirmMessage))
{
return;
}
@ -1195,4 +1188,4 @@ That's all! This is a fully working CRUD page, you can create, edit and delete t
## The Next Part
See the [next part](Part-10.md) of this tutorial.
See the [next part](Part-10.md) of this tutorial.

BIN
docs/en/Tutorials/images/blazor-bookstore-book-list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 60 KiB

BIN
docs/en/Tutorials/images/blazor-delete-book-action.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
docs/en/Tutorials/images/blazor-edit-book-action-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

135
docs/en/UI/Angular/Config-State-Service.md

@ -0,0 +1,135 @@
# Config State Service
`ConfigStateService` is a singleton service, i.e. provided in root level of your application, and keeps the application configuration response in the internal store.
## Before Use
In order to use the `ConfigStateService` you must inject it in your class as a dependency.
```js
import { ConfigStateService } from '@abp/ng.core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private config: ConfigStateService) {}
}
```
You do not have to provide the `ConfigStateService` at module or component/directive level, because it is already **provided in root**.
## Get Methods
`ConfigStateService` has numerous get methods which allow you to get a specific configuration or all configurations.
Get methods with "$" at the end of the method name (e.g. `getAll$`) return an RxJs stream. The streams are triggered when set or patched the state.
### How to Get All Configurations
You can use the `getAll` or `getAll$` method of `ConfigStateService` to get all of the applcation configuration response object. It is used as follows:
```js
// this.config is instance of ConfigStateService
const config = this.config.getAll();
// or
this.config.getAll$().subscribe(config => {
// use config here
})
```
### How to Get a Specific Configuration
You can use the `getOne` or `getOne$` method of `ConfigStateService` to get a specific configuration property. For that, the property name should be passed to the method as parameter.
```js
// this.config is instance of ConfigStateService
const currentUser = this.config.getOne("currentUser");
// or
this.config.getOne$("currentUser").subscribe(currentUser => {
// use currentUser here
})
```
On occasion, you will probably want to be more specific than getting just the current user. For example, here is how you can get the `tenantId`:
```js
const tenantId = this.config.getDeep("currentUser.tenantId");
// or
this.config.getDeep$("currentUser.tenantId").subscribe(tenantId => {
// use tenantId here
})
```
or by giving an array of keys as parameter:
```js
const tenantId = this.config.getDeep(["currentUser", "tenantId"]);
```
FYI, `getDeep` is able to do everything `getOne` does. Just keep in mind that `getOne` is slightly faster.
### How to Get a Feature
You can use the `getFeature` or `getFeature$` method of `ConfigStateService` to get a feature value. For that, the feature name should be passed to the method as parameter.
```js
// this.config is instance of ConfigStateService
const enableLdapLogin = this.configStateService.getFeature("Account.EnableLdapLogin");
// or
this.config.getFeature$("Account.EnableLdapLogin").subscribe(enableLdapLogin => {
// use enableLdapLogin here
})
```
> For more information, see the [features document](./Features).
### How to Get a Setting
You can use the `getSetting` or `getSetting$` method of `ConfigStateService` to get a setting. For that, the setting name should be passed to the method as parameter.
```js
// this.config is instance of ConfigStateService
const twoFactorBehaviour = this.configStateService.getSetting("Abp.Identity.TwoFactor.Behaviour");
// or
this.config.getSetting$("Abp.Identity.TwoFactor.Behaviour").subscribe(twoFactorBehaviour => {
// use twoFactorBehaviour here
})
```
> For more information, see the [settings document](./Settings).
#### State Properties
Please refer to `ApplicationConfiguration.Response` type for all the properties you can get with `getOne` and `getDeep`. It can be found in the [application-configuration.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts#L4).
## Set State
`ConfigStateService` has a method named `setState` which allow you to set the state value.
You can get the application configuration response and set the `ConfigStateService` state value as shown below:
```js
import {ApplicationConfigurationService, ConfigStateService} from '@abp/ng.core';
constructor(private applicationConfigurationService: ApplicationConfigurationService, private config: ConfigStateService) {
this.applicationConfigurationService.getConfiguration().subscribe(config => {
this.config.setState(config);
})
}
```
## See Also
- [Settings](./Settings.md)
- [Features](./Features.md)

193
docs/en/UI/Angular/Config-State.md

@ -1,192 +1 @@
# Config State
`ConfigStateService` is a singleton service, i.e. provided in root level of your application, and is actually a façade for interacting with application configuration state in the `Store`.
## Before Use
In order to use the `ConfigStateService` you must inject it in your class as a dependency.
```js
import { ConfigStateService } from '@abp/ng.core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private config: ConfigStateService) {}
}
```
You do not have to provide the `ConfigStateService` at module or component/directive level, because it is already **provided in root**.
## Selector Methods
`ConfigStateService` has numerous selector methods which allow you to get a specific configuration or all configurations from the `Store`.
### How to Get All Configurations From the Store
You can use the `getAll` method of `ConfigStateService` to get all of the configuration object from the store. It is used as follows:
```js
// this.config is instance of ConfigStateService
const config = this.config.getAll();
```
### How to Get a Specific Configuration From the Store
You can use the `getOne` method of `ConfigStateService` to get a specific configuration property from the store. For that, the property name should be passed to the method as parameter.
```js
// this.config is instance of ConfigStateService
const currentUser = this.config.getOne("currentUser");
```
On occasion, you will probably want to be more specific than getting just the current user. For example, here is how you can get the `tenantId`:
```js
const tenantId = this.config.getDeep("currentUser.tenantId");
```
or by giving an array of keys as parameter:
```js
const tenantId = this.config.getDeep(["currentUser", "tenantId"]);
```
FYI, `getDeep` is able to do everything `getOne` does. Just keep in mind that `getOne` is slightly faster.
#### Config State Properties
Please refer to `Config.State` type for all the properties you can get with `getOne` and `getDeep`. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L7).
### How to Get the Application Information From the Store
The `getApplicationInfo` method is used to get the application information from the environment variables stored as the config state. This is how you can use it:
```js
// this.config is instance of ConfigStateService
const appInfo = this.config.getApplicationInfo();
```
This method never returns `undefined` or `null` and returns an empty object literal (`{}`) instead. In other words, you will never get an error when referring to the properties of `appInfo` above.
#### Application Information Properties
Please refer to `Config.Application` type for all the properties you can get with `getApplicationInfo`. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L21).
### How to Get API URL From the Store
The `getApplicationInfo` method is used to get a specific API URL from the environment variables stored as the config state. This is how you can use it:
```js
// this.config is instance of ConfigStateService
const apiUrl = this.config.getApiUrl();
// environment.apis.default.url
const searchUrl = this.config.getApiUrl("search");
// environment.apis.search.url
```
This method returns the `url` of a specific API based on the key given as its only parameter. If there is no key, `'default'` is used.
### How to Get a Specific Permission From the Store
You can use the `getGrantedPolicy` method of `ConfigStateService` to get a specific permission from the configuration state. For that, you should pass a policy key as parameter to the method.
```js
// this.config is instance of ConfigStateService
const hasIdentityPermission = this.config.getGrantedPolicy("Abp.Identity");
// true
```
You may also **combine policy keys** to fine tune your selection:
```js
// this.config is instance of ConfigStateService
const hasIdentityAndAccountPermission = this.config.getGrantedPolicy(
"Abp.Identity && Abp.Account"
);
// false
const hasIdentityOrAccountPermission = this.config.getGrantedPolicy(
"Abp.Identity || Abp.Account"
);
// true
```
Please consider the following **rules** when creating your permission selectors:
- Maximum 2 keys can be combined.
- `&&` operator looks for both keys.
- `||` operator looks for either key.
- Empty string `''` as key will return `true`
- Using an operator without a second key will return `false`
### How to Get Translations From the Store
The `getLocalization` method of `ConfigStateService` is used for translations. Here are some examples:
```js
// this.config is instance of ConfigStateService
const identity = this.config.getLocalization("AbpIdentity::Identity");
// 'identity'
const notFound = this.config.getLocalization("AbpIdentity::IDENTITY");
// 'AbpIdentity::IDENTITY'
const defaultValue = this.config.getLocalization({
key: "AbpIdentity::IDENTITY",
defaultValue: "IDENTITY"
});
// 'IDENTITY'
```
Please check out the [localization documentation](./Localization.md) for details.
## Dispatch Methods
`ConfigStateService` has several dispatch methods which allow you to conveniently dispatch predefined actions to the `Store`.
### How to Get Application Configuration From Server
The `dispatchGetAppConfiguration` triggers a request to an endpoint that responds with the application state and then places this response to the `Store` as configuration state.
```js
// this.config is instance of ConfigStateService
this.config.dispatchGetAppConfiguration();
// returns a state stream which emits after dispatch action is complete
```
Note that **you do not have to call this method at application initiation**, because the application configuration is already being received from the server at start.
### How to Set the Environment
The `dispatchSetEnvironment` places environment variables passed to it in the `Store` under the configuration state. Here is how it is used:
```js
// this.config is instance of ConfigStateService
this.config.dispatchSetEnvironment({
/* environment properties here */
});
// returns a state stream which emits after dispatch action is complete
```
Note that **you do not have to call this method at application initiation**, because the environment variables are already being stored at start.
#### Environment Properties
Please refer to `Config.Environment` type for all the properties you can pass to `dispatchSetEnvironment` as parameter. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L13).
## See Also
- [Settings](./Settings.md)
- [Features](./Features.md)
**ConfigState has been deprecated.** Use the [ConfigStateService](./Config-State-Service) instead.

78
docs/en/UI/Angular/Environment.md

@ -101,3 +101,81 @@ export interface RemoteEnv {
* `customMergeFn`: You can also provide your own merge function as shown in the example. It will take two parameters, `localEnv: Partial<Config.Environment>` and `remoteEnv` and it needs to return a `Config.Environment` object.
* `method`: HTTP method to be used when retrieving environment config. Default: `GET`
* `headers`: If extra headers are needed for the request, it can be set through this field.
## EnvironmentService
` EnvironmentService` is a singleton service, i.e. provided in root level of your application, and keeps the environment in the internal store.
### Before Use
In order to use the `EnvironmentService` you must inject it in your class as a dependency.
```js
import { EnvironmentService } from '@abp/ng.core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private environment: EnvironmentService) {}
}
```
You do not have to provide the `EnvironmentService` at module or component/directive level, because it is already **provided in root**.
### Get Methods
`EnvironmentService` has numerous get methods which allow you to get a specific value or all environment object.
Get methods with "$" at the end of the method name (e.g. `getEnvironment$`) return an RxJs stream. The streams are triggered when set or patched the state.
#### How to Get Environment Object
You can use the `getEnvironment` or `getEnvironment$` method of `EnvironmentService` to get all of the environment object. It is used as follows:
```js
// this.environment is instance of EnvironmentService
const environment = this.environment.getAll();
// or
this.environment.getAll$().subscribe(environment => {
// use environment here
})
```
#### How to Get API URL
The `getApiUrl` or `getApiUrl$` method is used to get a specific API URL from the environment object. This is how you can use it:
```js
// this.environment is instance of EnvironmentService
const apiUrl = this.environment.getApiUrl();
// environment.apis.default.url
this.environment.getApiUrl$("search").subscribe(searchUrl => {
// environment.apis.search.url
})
```
This method returns the `url` of a specific API based on the key given as its only parameter. If there is no key, `'default'` is used.
#### How to Set the Environment
`EnvironmentService` has a method named `setState` which allow you to set the state value.
```js
// this.environment is instance of EnvironmentService
this.environment.setState(newEnvironmentObject);
```
Note that **you do not have to call this method at application initiation**, because the environment variables are already being stored at start.
#### Environment Properties
Please refer to `Environment` type for all the properties. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L13).

44
docs/en/UI/Angular/List-Service.md

@ -68,6 +68,50 @@ Bind `ListService` to ngx-datatable like this:
</ngx-datatable>
```
## Extending query with custom variables
You can extend the query parameter of the `ListService`'s `hookToQuery` method.
Firstly, you should pass your own type to `ListService` as shown below:
```typescript
constructor(public readonly list: ListService<BooksSearchParamsDto>) { }
```
Then update the `bookStreamCreator` constant like following:
```typescript
const bookStreamCreator = (query) => this.bookService.getList({...query, name: 'name here'});
```
You can also create your params object.
Define a variable like this:
```typescript
booksSearchParams = {} as BooksSearchParamsDto;
```
Update the `bookStreamCreator` constant:
```typescript
const bookStreamCreator = (query) => this.bookService.getList({...query, ...this.booksSearchParams});
```
Then you can place inputs to the HTML:
```html
<div class="form-group">
<input
class="form control"
placeholder="Name"
(keyup.enter)="list.get()"
[(ngModel)]="booksSearchParams.name"
/>
</div>
```
`ListService` emits the hookToQuery stream when you call the `this.list.get()` method.
## Usage with Observables

139
docs/en/UI/Angular/Localization.md

@ -100,36 +100,6 @@ this.localizationService.get('Resource::Key');
this.localizationService.get({ key: 'Resource::Key', defaultValue: 'Default Value' });
```
### Using the Config State
In order to you `getLocalization` method you should import ConfigState.
```js
import { ConfigState } from '@abp/ng.core';
```
Then you can use it as followed:
```js
this.store.selectSnapshot(ConfigState.getLocalization('ResourceName::Key'));
```
`getLocalization` method can be used with both `localization key` and [`LocalizationWithDefault`](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L34) interface.
```js
this.store.selectSnapshot(
ConfigState.getLocalization(
{
key: 'AbpIdentity::UserDeletionConfirmation',
defaultValue: 'Default Value',
},
'John',
),
);
```
Localization resources are stored in the `localization` property of `ConfigState`.
## RTL Support
As of v2.9 ABP has RTL support. If you are generating a new project with v2.9 and above, everything is set, you do not need to do any changes. If you are migrating your project from an earlier version, please follow the 2 steps below:
@ -193,44 +163,129 @@ import { Component } from '@angular/core';
export class AppComponent {}
```
## Mapping of Culture Name to Angular Locale File Name
## Registering a New Locale
Since ABP has more than one language, Angular locale files loads lazily using [Webpack's import function](https://webpack.js.org/api/module-methods/#import-1) to avoid increasing the bundle size and register to Angular core using the [`registerLocaleData`](https://angular.io/api/common/registerLocaleData) function. The chunks to be included in the bundle are specified by the [Webpack's magic comments](https://webpack.js.org/api/module-methods/#magic-comments) as hard-coded. Therefore a `registerLocale` function that returns Webpack `import` function must be passed to `CoreModule`.
### registerLocaleFn
`registerLocale` function that exported from `@abp/ng.core/locale` package is a higher order function that accepts `cultureNameLocaleFileMap` object and `errorHandlerFn` function as params and returns Webpack `import` function. A `registerLocale` function must be passed to the `forRoot` of the `CoreModule` as shown below:
```js
// app.module.ts
import { registerLocale } from '@abp/ng.core/locale';
// if you have commercial license and the language management module, add the below import
// import { registerLocale } from '@volo/abp.ng.language-management/locale';
@NgModule({
imports: [
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale(
// you can pass the cultureNameLocaleFileMap and errorHandlerFn as optionally
{
cultureNameLocaleFileMap: { 'pt-BR': 'pt' },
errorHandlerFn: ({ resolve, reject, locale, error }) => {
// the error can be handled here
},
},
)
}),
//...
]
```
### Mapping of Culture Name to Angular Locale File Name
Some of the culture names defined in .NET do not match Angular locales. In such cases, the Angular app throws an error like below at runtime:
![locale-error](./images/locale-error.png)
If you see an error like this, you should pass the `cultureNameLocaleFileMap` property like below to CoreModule's forRoot static method.
If you see an error like this, you should pass the `cultureNameLocaleFileMap` property like below to the `registerLocale` function.
```js
// app.module.ts
import { registerLocale } from '@abp/ng.core/locale';
// if you have commercial license and the language management module, add the below import
// import { registerLocale } from '@volo/abp.ng.language-management/locale';
@NgModule({
imports: [
// other imports
CoreModule.forRoot({
// other options
cultureNameLocaleFileMap: {
"DotnetCultureName": "AngularLocaleFileName",
"pt-BR": "pt" // example
}
})
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale(
{
cultureNameLocaleFileMap: {
"DotnetCultureName": "AngularLocaleFileName",
"pt-BR": "pt" // example
},
},
)
}),
//...
```
See [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales).
## Adding new culture
### Adding a New Culture
Add the below code to the `app.module.ts` by replacing `your-locale` placeholder with a correct locale name.
```js
//app.module.ts
import { storeLocaleData } from '@abp/ng.core';
import { storeLocaleData } from '@abp/ng.core/locale';
import(
/* webpackChunkName: "_locale-your-locale-js"*/
/* webpackMode: "eager" */
'@angular/common/locales/your-locale.js'
).then(m => storeLocaleData(m.default, 'your-locale'));
```
...or a custom `registerLocale` function can be passed to the `CoreModule`:
```js
// register-locale.ts
import { differentLocales } from '@abp/ng.core';
export function registerLocale(locale: string) {
return import(
/* webpackChunkName: "_locale-[request]"*/
/* webpackInclude: /[/\\](en|fr).js/ */
/* webpackExclude: /[/\\]global|extra/ */
`@angular/common/locales/${differentLocales[locale] || locale}.js`
)
}
// app.module.ts
import { registerLocale } from './register-locale';
@NgModule({
imports: [
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale
}),
//...
]
```
After this custom `registerLocale` function, since the en and fr added to the `webpackInclude`, only en and fr locale files will be created as chunks:
![locale chunks](https://user-images.githubusercontent.com/34455572/98203212-acaa2100-1f44-11eb-85af-4eb66d296326.png)
Which locale files you add to `webpackInclude` magic comment, they will be included in the bundle
## See Also
* [Localization in ASP.NET Core](../../Localization.md)

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

Loading…
Cancel
Save