Browse Source

Merge branch 'dev' into maliming/IdentityServer-v4

pull/4578/head
maliming 5 years ago
parent
commit
debdebba48
  1. 2
      .github/workflows/build-and-test.yml
  2. 7
      NuGet.Config
  3. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  4. 4
      build/common.ps1
  5. 2
      common.props
  6. 62
      docs/en/Application-Services.md
  7. 5
      docs/en/Best-Practices/Application-Services.md
  8. 271
      docs/en/Blog-Posts/2020-10-15 v3_3_Preview/POST.md
  9. BIN
      docs/en/Blog-Posts/2020-10-15 v3_3_Preview/abp-blazor-ui.png
  10. BIN
      docs/en/Blog-Posts/2020-10-15 v3_3_Preview/abp-commercial-blazor-ui.png
  11. BIN
      docs/en/Blog-Posts/2020-10-15 v3_3_Preview/abp-commercial-linked-users.png
  12. BIN
      docs/en/Blog-Posts/2020-10-15 v3_3_Preview/abp-commercial-setting-account-external-logins.png
  13. 119
      docs/en/CSRF-Anti-Forgery.md
  14. 477
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/POST.md
  15. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/bee.gif
  16. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/db-migrator-1.jpg
  17. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/db-migrator-2.jpg
  18. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/db-migrator-3.jpg
  19. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/email-last.jpg
  20. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/embedded-resource-2.jpg
  21. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/embedded-resource.jpg
  22. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/inline-content.png
  23. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/message.jpg
  24. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/settings.jpg
  25. BIN
      docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/template-definitions.png
  26. 160
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md
  27. BIN
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/added-new-migration.png
  28. BIN
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/custom-identity-user-list.png
  29. BIN
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/initial-project.png
  30. BIN
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/new-user.png
  31. BIN
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/nuget-package-manager.png
  32. BIN
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/user-table.png
  33. 35
      docs/en/Customizing-Application-Modules-Overriding-Services.md
  34. 65
      docs/en/Distributed-Event-Bus-Rebus-Integration.md
  35. 3
      docs/en/Distributed-Event-Bus.md
  36. 12
      docs/en/Entity-Framework-Core-SQLite.md
  37. 3
      docs/en/JavaScript/Dynamic-JavaScript-API-Clients.md
  38. 99
      docs/en/Localization.md
  39. 13
      docs/en/Migration-Guides/BlazorUI-3_3.md
  40. 2
      docs/en/Redis-Cache.md
  41. 49
      docs/en/Repositories.md
  42. 23
      docs/en/Settings.md
  43. 2
      docs/en/Tutorials/Part-10.md
  44. 10
      docs/en/Tutorials/Part-2.md
  45. 20
      docs/en/Tutorials/Part-3.md
  46. 3
      docs/en/Tutorials/Part-4.md
  47. 8
      docs/en/Tutorials/Part-5.md
  48. 4
      docs/en/Tutorials/Part-7.md
  49. 14
      docs/en/Tutorials/Part-9.md
  50. BIN
      docs/en/Tutorials/images/bookstore-authors-blazor-ui.png
  51. 11
      docs/en/UI/Angular/Localization.md
  52. 3
      docs/en/UI/Angular/Settings.md
  53. 96
      docs/en/UI/Angular/Toaster-Service.md
  54. 3
      docs/en/UI/AspNetCore/Client-Side-Package-Management.md
  55. 4
      docs/en/UI/AspNetCore/Customization-User-Interface.md
  56. 150
      docs/en/UI/AspNetCore/JavaScript-API/Ajax.md
  57. 24
      docs/en/UI/AspNetCore/JavaScript-API/Auth.md
  58. 56
      docs/en/UI/AspNetCore/JavaScript-API/Block-Busy.md
  59. 49
      docs/en/UI/AspNetCore/JavaScript-API/CurrentUser.md
  60. 117
      docs/en/UI/AspNetCore/JavaScript-API/DOM.md
  61. 3
      docs/en/UI/AspNetCore/JavaScript-API/Dynamic-JavaScript-Client-Proxies.md
  62. 88
      docs/en/UI/AspNetCore/JavaScript-API/Events.md
  63. 15
      docs/en/UI/AspNetCore/JavaScript-API/Features.md
  64. 32
      docs/en/UI/AspNetCore/JavaScript-API/Index.md
  65. 143
      docs/en/UI/AspNetCore/JavaScript-API/Localization.md
  66. 50
      docs/en/UI/AspNetCore/JavaScript-API/Logging.md
  67. 128
      docs/en/UI/AspNetCore/JavaScript-API/Message.md
  68. 45
      docs/en/UI/AspNetCore/JavaScript-API/Notify.md
  69. 40
      docs/en/UI/AspNetCore/JavaScript-API/ResourceLoader.md
  70. 33
      docs/en/UI/AspNetCore/JavaScript-API/Settings.md
  71. 106
      docs/en/UI/AspNetCore/Layout-Hooks.md
  72. 483
      docs/en/UI/AspNetCore/Modals.md
  73. 10
      docs/en/UI/AspNetCore/Tag-Helpers/Badges.md
  74. 18
      docs/en/UI/AspNetCore/Tag-Helpers/Blockquote.md
  75. 6
      docs/en/UI/AspNetCore/Tag-Helpers/Borders.md
  76. 2
      docs/en/UI/AspNetCore/Tag-Helpers/Breadcrumbs.md
  77. 2
      docs/en/UI/AspNetCore/Tag-Helpers/Button-groups.md
  78. 2
      docs/en/UI/AspNetCore/Tag-Helpers/Carousel.md
  79. 2
      docs/en/UI/AspNetCore/Tag-Helpers/Collapse.md
  80. 14
      docs/en/UI/AspNetCore/Tag-Helpers/Figure.md
  81. 9
      docs/en/UI/AspNetCore/Tag-Helpers/Index.md
  82. 4
      docs/en/UI/AspNetCore/Tag-Helpers/Modals.md
  83. 4
      docs/en/UI/AspNetCore/Tag-Helpers/Navs.md
  84. 2
      docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md
  85. 2
      docs/en/UI/AspNetCore/Tag-Helpers/Tables.md
  86. 3
      docs/en/UI/Blazor/Localization.md
  87. 3
      docs/en/UI/Blazor/Settings.md
  88. 83
      docs/en/docs-nav.json
  89. BIN
      docs/en/images/ajax-error.png
  90. BIN
      docs/en/images/js-message-confirm.png
  91. BIN
      docs/en/images/js-message-error.png
  92. BIN
      docs/en/images/js-message-success.png
  93. BIN
      docs/en/images/js-notify-success.png
  94. BIN
      docs/en/images/modal-example-product-create.png
  95. BIN
      docs/en/images/modal-example-product-info.png
  96. BIN
      docs/en/images/modal-manager-cancel-warning.png
  97. BIN
      docs/en/images/modal-manager-example-modal.png
  98. BIN
      docs/en/images/modal-manager-validation.png
  99. BIN
      docs/en/images/modal-page-on-rider.png
  100. BIN
      docs/en/images/product-create-modal-page-on-rider.png

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-rc.2.20479.15
- name: Build All
run: .\build-all.ps1

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>

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

@ -83,6 +83,7 @@
"LatestBlogPost": "Latest Blog Post",
"Edit": "Edit",
"ProfileImageChange": "Change the profile image",
"BlogItemErrorMessage": "Could not get the latest blog post details from ABP."
"BlogItemErrorMessage": "Could not get the latest blog post details from ABP.",
"PlannedReleaseDate": "Planned release date"
}
}

4
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")

2
common.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>3.3.0</Version>
<Version>4.0.0</Version>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>

62
docs/en/Application-Services.md

@ -445,6 +445,68 @@ These methods are used to convert Entities to DTOs and vice verse. They uses the
* `MapToEntityAsync(TCreateInput)` is used to create an entity from `TCreateInput`.
* `MapToEntityAsync(TUpdateInput, TEntity)` is used to update an existing entity from `TUpdateInput`.
## Miscellaneous
### Working with Streams
`Stream` object itself is not serializable. So, you may have problems if you directly use `Stream` as the parameter or the return value for your application service. ABP Framework provides a special type, `IRemoteStreamContent` to be used to get or return streams in the application services.
**Example: Application Service Interface that can be used to get and return streams**
````csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Content;
namespace MyProject.Test
{
public interface ITestAppService : IApplicationService
{
Task Upload(Guid id, IRemoteStreamContent streamContent);
Task<IRemoteStreamContent> Download(Guid id);
}
}
````
**Example: Application Service Implementation that can be used to get and return streams**
````csharp
using System;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Content;
namespace MyProject.Test
{
public class TestAppService : ApplicationService, ITestAppService
{
public Task<IRemoteStreamContent> Download(Guid id)
{
var fs = new FileStream("C:\\Temp\\" + id + ".blob", FileMode.OpenOrCreate);
return Task.FromResult(
(IRemoteStreamContent) new RemoteStreamContent(fs) {
ContentType = "application/octet-stream"
}
);
}
public async Task Upload(Guid id, IRemoteStreamContent streamContent)
{
using (var fs = new FileStream("C:\\Temp\\" + id + ".blob", FileMode.Create))
{
await streamContent.GetStream().CopyToAsync(fs);
await fs.FlushAsync();
}
}
}
}
````
`IRemoteStreamContent` is compatible with the [Auto API Controller](API/Auto-API-Controllers.md) and [Dynamic C# HTTP Proxy](API/Dynamic-CSharp-API-Clients.md) systems.
## Lifetime
Lifetime of application services are [transient](Dependency-Injection.md) and they are automatically registered to the dependency injection system.

5
docs/en/Best-Practices/Application-Services.md

@ -213,6 +213,11 @@ This method votes a question and returns the current score of the question.
* **Do** always get all the related entities from repositories to perform the operations on them.
* **Do** call repository's Update/UpdateAsync method after updating an entity. Because, not all database APIs support change tracking & auto update.
#### Handle files
* **Do not** use any web components like `IFormFile` or `Stream` in the application services. If you want to serve a file you can use `byte[]`.
* **Do** use a `Controller` to handle file uploading then pass the `byte[]` of the file to the application service method.
#### Using Other Application Services
* **Do not** use other application services of the same module/application. Instead;

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

@ -0,0 +1,271 @@
# ABP Framework 3.3 RC Has Been Published
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
We had released an experimental early preview version of the Blazor UI with the [previous version](https://blog.abp.io/abp/ABP-Framework-ABP-Commercial-3.2-RC-With-The-New-Blazor-UI). In this version, we've completed most of the fundamental infrastructure features and the application modules (like identity and tenant management).
It currently has almost the same functionalities as the other UI types (Angular & MVC / Razor Pages).
**Example screenshot**: User management page of the Blazor UI
![abp-blazor-ui](abp-blazor-ui.png)
> We've adapted the [Lepton Theme](https://commercial.abp.io/themes) for the ABP Commercial, see the related section below.
We are still working on the fundamentals. So, the next version may introduce breaking changes of the Blazor UI. We will work hard to keep them with the minimal effect on your application code.
#### 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://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. So, no configuration needed, just upgrade the ABP Framework.
[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://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
You have a problem when you want to use **Async LINQ Extension Methods** (e.g. `FirstOrDefaultAsync(...)`) in your **domain** and **application** layers. These async methods are **not included in the standard LINQ extension methods**. Those are defined by the [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore) NuGet package (see [the code](https://github.com/dotnet/efcore/blob/main/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs)). To be able to use these `async` methods, you need to reference to the `Microsoft.EntityFrameworkCore` package.
If you don't want to depend on the EF Core in your business layer, then ABP Framework provides the `IAsyncQueryableExecuter` service to execute your queries asynchronously without depending on the EF Core package. You can see [the documentation](https://docs.abp.io/en/abp/latest/Repositories#option-3-iasyncqueryableexecuter) to get more information about this service.
ABP Framework version 3.3 takes this one step further and allows you to directly execute the async LINQ extension methods on the `IRepository` interface.
**Example: Use `CountAsync` and `FirstOrDefaultAsync` methods on the repositories**
````csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace MyCompanyName.MyProjectName
{
public class BookAppService : ApplicationService, IBookAppService
{
private readonly IRepository<Book, Guid> _bookRepository;
public BookAppService(IRepository<Book, Guid> bookRepository)
{
_bookRepository = bookRepository;
}
public async Task DemoAsync()
{
var countAll = await _bookRepository
.CountAsync();
var count = await _bookRepository
.CountAsync(x => x.Name.Contains("A"));
var book1984 = await _bookRepository
.FirstOrDefaultAsync(x => x.Name == "1984");
}
}
}
````
All the standard LINQ methods are supported: *AllAsync, AnyAsync, AverageAsync, ContainsAsync, CountAsync, FirstAsync, FirstOrDefaultAsync, LastAsync, LastOrDefaultAsync, LongCountAsync, MaxAsync, MinAsync, SingleAsync, SingleOrDefaultAsync, SumAsync, ToArrayAsync, ToListAsync*.
This approach still has a limitation. You need to execute the extension method directly on the repository object. For example, the below usage is **not supported**:
````csharp
var count = await _bookRepository.Where(x => x.Name.Contains("A")).CountAsync();
````
This is because the object returned from the `Where` method is not a repository object, it is a standard `IQueryable`. In such cases, you can still use the `IAsyncQueryableExecuter`:
````csharp
var count = await AsyncExecuter.CountAsync(
_bookRepository.Where(x => x.Name.Contains("A"))
);
````
`AsyncExecuter` has all the standard extension methods, so you don't have any restriction here. See [the repository documentation](https://docs.abp.io/en/abp/latest/Repositories#iqueryable-async-operations) for all the options you have.
> ABP Framework does its best to not depend on the EF Core and still be able to use the async LINQ extension methods. However, there is no problem to depend on the EF Core for your application, you can add the `Microsoft.EntityFrameworkCore` NuGet package and use the native methods.
### Stream Support for the Application Service Methods
[Application services](https://docs.abp.io/en/abp/latest/Application-Services) are consumed by clients and the parameters and return values (typically [Data Transfer Objects](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects)). In case of the client is a remote application, then these objects should be serialized & deserialized.
Until the version 3.3, we hadn't suggest to use the `Stream` in the application service contracts, since it is not serializable/deserializable. However, with the version 3.3, ABP Framework properly supports this scenario by introducing the new `IRemoteStreamContent` interface.
Example: An application service that can get or return streams
````csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Content;
namespace MyProject.Test
{
public interface ITestAppService : IApplicationService
{
Task Upload(Guid id, IRemoteStreamContent streamContent);
Task<IRemoteStreamContent> Download(Guid id);
}
}
````
The implementation can be as shown below:
````csharp
using System;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Content;
namespace MyProject.Test
{
public class TestAppService : ApplicationService, ITestAppService
{
public Task<IRemoteStreamContent> Download(Guid id)
{
var fs = new FileStream("C:\\Temp\\" + id + ".blob", FileMode.OpenOrCreate);
return Task.FromResult(
(IRemoteStreamContent) new RemoteStreamContent(fs) {
ContentType = "application/octet-stream"
}
);
}
public async Task Upload(Guid id, IRemoteStreamContent streamContent)
{
using (var fs = new FileStream("C:\\Temp\\" + id + ".blob", FileMode.Create))
{
await streamContent.GetStream().CopyToAsync(fs);
await fs.FlushAsync();
}
}
}
}
````
> This is just a demo code. Do it better in your production code :)
Thanks to [@alexandru-bagu](https://github.com/alexandru-bagu) for the great contribution!
### Other Changes
* Upgraded all the .NET Core / ASP.NET Core related packages to the version 3.1.8. If you have additional dependencies to the .NET Core / ASP.NET Core related packages, we suggest you to updates your packages to the version 3.1.8 to have the latest bug and security fixes published by Microsoft.
* The blogging module now uses the [BLOB Storing](https://docs.abp.io/en/abp/latest/Blob-Storing) system to store images & files of the blog posts. If you are using this module, then you need to manually migrate the local files to the BLOB Storing system after the upgrade.
* The Angular UI is now redirecting to the profile management page of the MVC UI instead of using its own UI, if you've configured the authorization code flow (which is default since the version 3.2.0).
## What's new with the ABP Commercial 3.3
### The Blazor UI
We have good news for the ABP Commercial Blazor UI too. We have implemented the [Lepton Theme](https://commercial.abp.io/themes) integration, so it is now available with the Blazor UI. Also, implemented most of the fundamental [modules](https://commercial.abp.io/modules).
**A screenshot from the ABP Commercial startup template with the Blazor UI**
![abp-commercial-blazor-ui](abp-commercial-blazor-ui.png)
There are still missing features and modules. However, we are working on it to have a more complete version in the next release.
#### 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://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
[Account module](https://commercial.abp.io/modules/Volo.Account.Pro) now supports to manage the social/external logins in the UI. You can **enable/disable** and **set options** in the settings page. It also supports to use **different credentials for the tenants** and it is also **configured on the runtime**.
![abp-commercial-setting-account-external-logins](abp-commercial-setting-account-external-logins.png)
### Linked Accounts
Linked user system allows you to link other accounts (including account in a different tenant) with your account, so you can switch between different accounts with a single-click. It is practical since you no longer need to logout and login again with entering the credentials of the target account.
To manage the linked accounts, go to the profile management page from the user menu;
![abp-commercial-linked-users](abp-commercial-linked-users.png)
### Paypal & Stripe Integrations
The [Payment Module](https://commercial.abp.io/modules/Volo.Payment) was supporting PayU and 2Checkout providers until the version 3.3. It's now integrated to PayPal and Stripe. See the [technical documentation](https://docs.abp.io/en/commercial/latest/modules/payment) to learn how to use it.
### ABP Suite Improvements
We've done a lot of small improvements for the [ABP Suite](https://commercial.abp.io/tools/suite). Some of the enhancements are;
* Show the previously installed modules as *installed* on the module list.
* Switch between the latest stable, the latest [preview](https://docs.abp.io/en/abp/latest/Previews) and the latest [nightly build](https://docs.abp.io/en/abp/latest/Nightly-Builds) versions of the ABP related packages.
* Moved the file that stores the *previously created entities* into the solution folder to allow you to store it in your source control system.
### Others
* Added an option to the Account Module to show reCAPTCHA on the login & the registration forms.
Besides the new features introduced in this post, we've done a lot of small other enhancements and bug fixes to provide a better development experience and increase the developer productivity.
## New Articles
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))
* [Using the AdminLTE Theme with the ABP Framework MVC / Razor Pages UI](https://community.abp.io/articles/using-the-adminlte-theme-with-the-abp-framework-mvc-razor-pages-ui-gssbhb7m) (by [@mucahiddanis](https://community.abp.io/members/mucahiddanis))
* [Using DevExtreme Angular Components With the ABP Framework](https://community.abp.io/articles/using-devextreme-angular-components-with-the-abp-framework-x5nyvj3i) (by [@bunyamin](https://community.abp.io/members/bunyamin))
It is appreciated if you want to [submit an article](https://community.abp.io/articles/submit) related to the ABP Framework.
## About the Next Release
The next version will be `4.0.0`. We are releasing a major version, since we will move the ABP Framework to .NET 5.0. We see that for most of the applications this will not be a breaking change and we hope you easily upgrade to it.
The planned 4.0.0-rc.1 (Release Candidate) version date is **November 11**, just after the Microsoft releases the .NET 5.0 final. The planned 4.0.0 final release date is **November 26**.
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 3.3.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 [3.3.0 final](https://github.com/abpframework/abp/milestone/44) version is October 27th.

BIN
docs/en/Blog-Posts/2020-10-15 v3_3_Preview/abp-blazor-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/en/Blog-Posts/2020-10-15 v3_3_Preview/abp-commercial-blazor-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
docs/en/Blog-Posts/2020-10-15 v3_3_Preview/abp-commercial-linked-users.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/Blog-Posts/2020-10-15 v3_3_Preview/abp-commercial-setting-account-external-logins.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

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

@ -0,0 +1,119 @@
# CSRF/XSRF & Anti Forgery System
"*Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a malicious web site, email, blog, instant message, or program causes a user’s web browser to perform an unwanted action on a trusted site for which the user is currently authenticated*" ([OWASP](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet)).
**ABP Framework completely automates CSRF preventing** and works out of the box without any configuration. Read this documentation only if you want to understand it better or need to customize.
## The Problem
ASP.NET Core [provides infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery) to prevent CSRF attacks by providing a system to **generate** and **validate antiforgery tokens**. However, the standard implementation has a few drawbacks;
Antiforgery token validation is only **enabled for razor pages by default** and not enabled for **HTTP APIs**. You need to enable it yourself for the Controllers. You can use the `[ValidateAntiForgeryToken]` attribute for a specific API Controller/Action or the `[AutoValidateAntiforgeryToken]` attribute to prevent attacks globally.
Once you enable it;
* You need to manually add an HTTP header, named `RequestVerificationToken` to every **AJAX request** made in your application. You should care about obtaining the token, saving in the client side and adding to the HTTP header on every HTTP request.
* All your clients, including **non-browser clients**, should care about obtaining and sending the antiforgery token in every request. In fact, non-browser clients has no CSRF risk and should not care about this.
Especially, the second point is a pain for your clients and unnecessarily consumes your server resources.
> You can read more about the ASP.NET Core antiforgery system in its own [documentation](https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery).
## The Solution
ABP Framework provides `[AbpValidateAntiForgeryToken]` and `[AbpAutoValidateAntiforgeryToken]` attributes, just like the attributes explained above. `[AbpAutoValidateAntiforgeryToken]` is already added to the global filters, so you should do nothing to enable it for your application.
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.
* 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.
That's all. The systems works smoothly.
## Configuration / Customization
### AbpAntiForgeryOptions
`AbpAntiForgeryOptions` is the main [options class](Options.md) to configure the ABP Antiforgery system. It has the following properties;
* `TokenCookie`: Can be used to configure the cookie details. This cookie is used to store the antiforgery token value in the client side, so clients can read it and sends the value as the HTTP header. Default cookie name is `XSRF-TOKEN`, expiration time is 10 years (yes, ten years! It should be a value longer than the authentication cookie max life time, for the security).
* `AuthCookieSchemaName`: The name of the authentication cookie used by your application. Default value is `Identity.Application` (which becomes `AspNetCore.Identity.Application` on runtime). The default value properly works with the ABP startup templates. **If you change the authentication cookie name, you also must change this.**
* `AutoValidate`: The single point to enable/disable the ABP automatic antiforgery validation system. Default value is `true`.
* `AutoValidateFilter`: A predicate that gets a type and returns a boolean. ABP uses this predicate to check a controller type. If it returns false for a controller type, the controller is excluded from the automatic antiforgery token validation.
* `AutoValidateIgnoredHttpMethods`: A list of HTTP Methods to ignore on automatic antiforgery validation. Default value: "GET", "HEAD", "TRACE", "OPTIONS". These HTTP Methods are safe to skip antiforgery validation since they don't change the application state.
If you need to change these options, do it in the `ConfigureServices` method of your [module](Module-Development-Basics.md).
**Example: Configuring the AbpAntiForgeryOptions**
```csharp
Configure<AbpAntiForgeryOptions>(options =>
{
options.TokenCookie.Expiration = TimeSpan.FromDays(365);
options.AutoValidateIgnoredHttpMethods.Remove("GET");
options.AutoValidateFilter =
type => !type.Namespace.StartsWith("MyProject.MyIgnoredNamespace");
});
```
This configuration;
* Sets the antiforgery token expiration time to ~1 year.
* Enables antiforgery token validation for GET requests too.
* Ignores the controller types in the specified namespace.
### AntiforgeryOptions
`AntiforgeryOptions` is the standard [options class](Options.md) of the ASP.NET Core. **You can find all the information about this class in its [own documentation](https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery)**.
`HeaderName` option is especially important for the ABP Framework point of view. Default value of this value is `RequestVerificationToken` and the clients uses this name while sending the token value in the header. So, if you change this option, you should also arrange your clients to align the change. If you don't have a good reason, leave it as default.
### AbpValidateAntiForgeryToken Attribute
If you disable the automatic validation or want to perform the validation for an endpoint that is not validated by default (for example, an endpoint with HTTP GET Method), you can use the `[AbpValidateAntiForgeryToken]` attribute for a **controller type or method** (action).
**Example: Add `[AbpValidateAntiForgeryToken]` to a HTTP GET method**
```csharp
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
namespace MyCompanyName.MyProjectName.Controllers
{
[Route("api/products")]
public class ProductController : AbpController
{
[HttpGet]
[AbpValidateAntiForgeryToken]
public async Task GetAsync()
{
//TODO: ...
}
}
}
```
### Angular UI
Angular supports CSRF Token out of box, but the token header name is `X-XSRF-TOKEN`. Since ABP Framework follows the ASP.NET Core conventions, it changes this value to `RequestVerificationToken` in the core package.
You don't need to make anything unless you need to change the `AntiforgeryOptions.HeaderName` as explained before. If you change it, remember to change the header name for the Angular application too. To do that, add an import declaration for the `HttpClientXsrfModule` into your root module.
**Example: Change the header name to *MyCustomHeaderName***
```typescript
@NgModule({
// ...
imports: [
//...
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'MyCustomHeaderName'
})
],
})
export class AppModule {}
```

477
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/POST.md

@ -0,0 +1,477 @@
# Replacing Email Templates and Sending Emails
## Introduction
Hi, in this step by step article, we will send an email by using standard email template and then we will replace the standard email template with our new created template, thanks to [Text Templating System](https://docs.abp.io/en/abp/latest/Text-Templating#replacing-the-existing-templates) and [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System). Let's start by explaining what these systems do.
* ABP framework provides a strong and flexible [Text Templating System](https://docs.abp.io/en/abp/latest/Text-Templating). So, we can use the text templating system to create dynamic email contents on a template and a model.
* In this article, we will use `StandardEmailTemplates.Message` as standard email template. Then we will create a new template and replace the standard email template with our new template by using [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System).
* The `Virtual File System` makes it possible to manage files that do not physically exist on the file system. That means we can override `StandardEmailTemplates.Message` template by changing it's path with our new template's path.
## Creating the Solution
> ABP Framework offers startup templates to get into the business faster.
In this article, I will create a new startup template and perform the operations on this template. But if you already have a project you don't need to create a new startup template, you can implement the following steps to your existing project. (These steps can be applied to any project. (MVC, Angular etc.))
> If you have already a project you can skip this section.
Before starting to development, we will create a solution named `TemplateReplace` (or whatever you want). We can create a new startup template by using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) :
````bash
abp new TemplateReplace
````
Our project boilerplate will be ready after the download is finished. Then, open the solution in the Visual Studio (or your favorite IDE).
Run the `TemplateReplace.DbMigrator` application as below to create the database and seed initial data (which creates the admin user, admin role, permissions etc.).
![db-migrator-1](db-migrator-1.jpg)
* Right click to `TemplateReplace.DbMigrator` and choose the `Debug`.
![db-migrator-2](db-migrator-2.jpg)
* After that, click the `Start new instance` option to start the database migrations.
![db-migrator-3](db-migrator-3.jpg)
Then we can run the `TemplateReplace.Web` project to see our application working.
> _Default login credentials for admin: username is **admin** and password is **1q2w3E\***_
## Starting the Development
First thing we need to do is, creating a email service to sending emails. ABP Framework provides `IEmailSender` service that is used to send emails.
### Step - 1
Create an `Emailing` folder in the `TemplateReplace.Domain` project and add a class named `EmailService` inside of it.
```csharp
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.TextTemplating;
namespace TemplateReplace.Emailing
{
public class EmailService : ITransientDependency
{
private readonly IEmailSender _emailSender;
private readonly ITemplateRenderer _templateRenderer;
public EmailService(IEmailSender emailSender, ITemplateRenderer templateRenderer)
{
_emailSender = emailSender;
_templateRenderer = templateRenderer;
}
public async Task SendAsync(string targetEmail)
{
var emailBody = await _templateRenderer.RenderAsync(
StandardEmailTemplates.Message,
new
{
message = "ABP Framework provides IEmailSender service that is used to send emails."
}
);
await _emailSender.SendAsync(
targetEmail,
"Subject",
emailBody
);
}
}
}
```
* To create an email content, we need to inject `ITemplateRenderer` and use the `RenderAsync` method to render a template.
* We've used `StandardEmailTemplates.Message` as standart email template. This provides us a standard and simple message template to send mails.
* The resulting email body should be like shown below:
```html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
ABP Framework provides IEmailSender service that is used to send emails.
</body>
</html>
```
### Step - 2 (Configuring Email Settings)
* Now, we need to configure some email settings by following [settings documentation](https://docs.abp.io/en/abp/latest/Settings#setting-values-in-the-application-configuration). For achieve this, open the `appsettings.json` file under `TemplateReplace.Web` and configure your email settings in **settings** section like below.
![appsettings.json](settings.jpg)
* Here, I used Google's SMTP settings to send emails via Gmail. You can change these setting values by your need.
> **Note:** If you want to use Google's SMTP server settings and send emails via Gmail, you should confirm [this](https://myaccount.google.com/u/0/lesssecureapps).
### Step - 3
* After that we need to open `TemplateReplaceDomainModule.cs` file and change its contents as below to sending real-time emails.
```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using TemplateReplace.MultiTenancy;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Emailing;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.IdentityServer;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.PermissionManagement.IdentityServer;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace TemplateReplace
{
[DependsOn(
typeof(TemplateReplaceDomainSharedModule),
typeof(AbpAuditLoggingDomainModule),
typeof(AbpBackgroundJobsDomainModule),
typeof(AbpFeatureManagementDomainModule),
typeof(AbpIdentityDomainModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(AbpIdentityServerDomainModule),
typeof(AbpPermissionManagementDomainIdentityServerModule),
typeof(AbpSettingManagementDomainModule),
typeof(AbpTenantManagementDomainModule),
typeof(AbpEmailingModule)
)]
public class TemplateReplaceDomainModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var settingManager = context.ServiceProvider.GetService<SettingManager>();
//encrypts the password on set and decrypts on get
settingManager.SetGlobalAsync(EmailSettingNames.Smtp.Password, "your_password");
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MultiTenancyConsts.IsEnabled;
});
// #if DEBUG
// context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
// #endif
}
}
}
```
* `NullEmailSender` is a built-in class that implements the `IEmailSender`, but writes email contents to the standard log system, rather than actually sending the emails. This class can be useful especially in development time where you generally don't want to send real emails. Therefore ABP framework defined this by default. But in our case we want to send real emails, so we must remove these lines or we must take it to the comment line.
* `Abp.Mailing.Smtp.Password` must be an encrypted value. Therefore we used `SettingManager` in here to set the password. It internally **encrypts** the values on set and **decrypts** on get.
* After all these steps, whenever we want to send an email, we can do it by using our `EmailService` class. We can inject this class and invoke the `SendAsync` method to sending email where its needed.
After sending the email we should see the template like below.
![email-message](message.jpg)
### Step - 4 (Defining New Template)
* So far we've sent mail by using standard email template of ABP. But we may want to replace the email template with the new one. We can achieve this by following the `Text Templating` [documentation](https://docs.abp.io/en/abp/latest/Text-Templating#replacing-the-existing-templates).
* In this article, I will create a email template by using free template generator named **Bee**. You can reach the free templates from [here](https://beefree.io/templates/free/).
* When we find a template for our purpose, we can hover the link and click the **get started** button to edit the template. (I chose a template named "gdpr".)
* Here, you can edit your template as below. (You can delete or add sections, edit texts, and so on.)
![bee](bee.gif)
> **Note:** After editing our template, we need to export it to reach our created template's content. You can see the **export** button top-right of the template editing page.
* After choosing and editing our free template, we can create a new **email template** in our project. For this, create a folder named `Templates` under `Emailing` folder in `TemplateReplace.Domain` and add `EmailTemplate.tpl` file inside of it. And copy-paste the below content or your template's content.
```tpl
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<meta content="width=device-width" name="viewport"/>
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
<style type="text/css">
body {
margin: 0;
padding: 0;
}
table,
td,
tr {
vertical-align: top;
border-collapse: collapse;
}
* {
line-height: inherit;
}
a[x-apple-data-detectors=true] {
color: inherit !important;
text-decoration: none !important;
}
</style>
<style id="media-query" type="text/css">
@media (max-width: 670px) {
.block-grid,
.col {
min-width: 320px !important;
max-width: 100% !important;
display: block !important;
}
.block-grid {
width: 100% !important;
}
.col {
width: 100% !important;
}
.col>div {
margin: 0 auto;
}
img.fullwidth,
img.fullwidthOnMobile {
max-width: 100% !important;
}
.no-stack .col {
min-width: 0 !important;
display: table-cell !important;
}
.no-stack.two-up .col {
width: 50% !important;
}
.no-stack .col.num2 {
width: 16.6% !important;
}
.no-stack .col.num3 {
width: 25% !important;
}
.no-stack .col.num4 {
width: 33% !important;
}
.no-stack .col.num5 {
width: 41.6% !important;
}
.no-stack .col.num6 {
width: 50% !important;
}
.no-stack .col.num7 {
width: 58.3% !important;
}
.no-stack .col.num8 {
width: 66.6% !important;
}
.no-stack .col.num9 {
width: 75% !important;
}
.no-stack .col.num10 {
width: 83.3% !important;
}
.video-block {
max-width: none !important;
}
.mobile_hide {
min-height: 0px;
max-height: 0px;
max-width: 0px;
display: none;
overflow: hidden;
font-size: 0px;
}
.desktop_hide {
display: block !important;
max-height: none !important;
}
}
</style>
</head>
<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: #3d1554;">
<table bgcolor="#3d1554" cellpadding="0" cellspacing="0" class="nl-container" role="presentation" style="table-layout: fixed; vertical-align: top; min-width: 320px; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #3d1554; width: 100%;" valign="top" width="100%">
<tbody>
<tr style="vertical-align: top;" valign="top">
<td style="word-break: break-word; vertical-align: top;" valign="top">
<div style="background-color:transparent;overflow:hidden">
<div class="block-grid" style="min-width: 320px; max-width: 650px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; Margin: 0 auto; width: 100%; background-color: transparent;">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:transparent;">
<div class="col num12" style="min-width: 320px; max-width: 650px; display: table-cell; vertical-align: top; width: 650px;">
<div style="width:100% !important;">
<div style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:35px; padding-bottom:0px; padding-right: 0px; padding-left: 0px;">
<div align="center" class="img-container center autowidth" style="padding-right: 0px;padding-left: 0px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="background-color:transparent;overflow:hidden">
<div class="block-grid" style="min-width: 320px; max-width: 650px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; Margin: 0 auto; width: 100%; background-color: transparent;">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:transparent;">
<div class="col num12" style="min-width: 320px; max-width: 650px; display: table-cell; vertical-align: top; width: 642px;">
<div style="width:100% !important;">
<div style="border-top:0px solid transparent; border-left:4px solid #57366E; border-bottom:0px solid transparent; border-right:4px solid #57366E; padding-top:55px; padding-bottom:60px; padding-right: 0px; padding-left: 0px;">
<div style="color:#fbd711;font-family:Poppins, Arial, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
<div style="line-height: 1.2; font-size: 12px; color: #fbd711; font-family: Poppins, Arial, Helvetica, sans-serif; mso-line-height-alt: 14px;">
<p style="font-size: 14px; line-height: 1.2; word-break: break-word; text-align: center; mso-line-height-alt: 17px; margin: 0;"><strong><span style="font-size: 30px;">ABP Community </span></strong></p>
</div>
</div>
<div style="color:#ffffff;font-family:Poppins, Arial, Helvetica, sans-serif;line-height:1.8;padding-top:10px;padding-right:50px;padding-bottom:10px;padding-left:50px;">
<div style="line-height: 1.8; font-size: 12px; color: #ffffff; font-family: Poppins, Arial, Helvetica, sans-serif; mso-line-height-alt: 22px;">
<p style="line-height: 1.8; word-break: break-word; font-size: 14px; mso-line-height-alt: 25px; margin: 0;"><span style="font-size: 14px;">Share your experiences with the ABP Framework!</span><br/><span style="font-size: 14px;">ABP is an open source and community driven project. This guide is aims to help anyone wants to contribute to the project.</span></p>
<p style="line-height: 1.8; word-break: break-word; font-size: 14px; mso-line-height-alt: 25px; margin: 0;"><span style="font-size: 14px;">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 web site.</span></p>
</div>
</div>
<div align="center" class="button-container" style="padding-top:12px;padding-right:10px;padding-bottom:12px;padding-left:10px;">
<a href="http://www.example.com/" style="-webkit-text-size-adjust: none; text-decoration: none; display: inline-block; color: #000000; background-color: #fbd711; border-radius: 30px; -webkit-border-radius: 30px; -moz-border-radius: 30px; width: auto; width: auto; border-top: 1px solid #fbd711; border-right: 1px solid #fbd711; border-bottom: 1px solid #fbd711; border-left: 1px solid #fbd711; padding-top: 10px; padding-bottom: 10px; font-family: Poppins, Arial, Helvetica, sans-serif; text-align: center; mso-border-alt: none; word-break: keep-all;" target="_blank"><span style="padding-left:45px;padding-right:45px;font-size:18px;display:inline-block;"><span style="font-size: 16px; line-height: 2; word-break: break-word; mso-line-height-alt: 32px;"><span data-mce-style="font-size: 18px; line-height: 36px;" style="font-size: 18px; line-height: 36px;"><strong>Contribute</strong></span></span></span></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
```
* Then we need to make the template file as "Embedded Resource". We can do this as below.
* First right click to **EmailTemplate.tpl** and choose `Properties`.
![embedded-resource](embedded-resource.jpg)
* Then be sure about build action is **Embedded resource**.
![embedded-resource-2](embedded-resource-2.jpg)
### Step - 4 (Replacing the Email Template)
* To replace the current email template with our new email template, we need to override it. To achieve this, create a class named `EmailTemplateDefinitionProvider` under `Emailing` folder in `TemplateReplace.Domain` and fill it with the below content.
```csharp
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.TextTemplating;
namespace TemplateReplace.Emailing
{
public class EmailTemplateDefinitionProvider : TemplateDefinitionProvider, ITransientDependency
{
public override void Define(ITemplateDefinitionContext context)
{
var emailLayoutTemplate = context.GetOrNull(StandardEmailTemplates.Message);
emailLayoutTemplate
.WithVirtualFilePath(
"/Emailing/Templates/EmailTemplate.tpl",
isInlineLocalized: true
);
}
}
}
```
* In here we've created a template definition provider class that gets the email layout template and change the virtual file path for the template.
* This approach allows us to locate templates in any folder instead of the folder defined by the depended module. For more detail, check the [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System).
### Step - 5
* Lastly, we need to configure the [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System). To do this open your `TemplateReplaceDomainModule.cs` in `TemplateReplace.Domain` and update the content as below.
```csharp
using TemplateReplace.MultiTenancy;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Emailing;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.IdentityServer;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.PermissionManagement.IdentityServer;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
using Volo.Abp.VirtualFileSystem;
namespace TemplateReplace
{
[DependsOn(
typeof(TemplateReplaceDomainSharedModule),
typeof(AbpAuditLoggingDomainModule),
typeof(AbpBackgroundJobsDomainModule),
typeof(AbpFeatureManagementDomainModule),
typeof(AbpIdentityDomainModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(AbpIdentityServerDomainModule),
typeof(AbpPermissionManagementDomainIdentityServerModule),
typeof(AbpSettingManagementDomainModule),
typeof(AbpTenantManagementDomainModule),
typeof(AbpEmailingModule)
)]
public class TemplateReplaceDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MultiTenancyConsts.IsEnabled;
});
//Add this configuration
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<TemplateReplaceDomainModule>();
});
}
}
}
```
* And now when we send a new email, we should see our newly defined template as the message like below.
![email-last](email-last.jpg)
## Text Template Management
* Generally, more than one e-mail is required in applications. We create email templates for **"password changes"** or **"welcome"** etc in our applications. In such cases, it is necessary to create different templates for each mail. ABP Commercial allows us to perform these operations on UI in a simple way. Text Template Management provides UI to easily create and manage email templates.
![template-definitions](template-definitions.png)
* ABP Commercial's [Text Template Management](https://commercial.abp.io/modules/Volo.TextTemplateManagement) module is really fascinating. It makes it super easy to stores and edits template contents. We can list all templates on a page, editing them, localizing them, and so on.
![inline-content](inline-content.png)
* ABP Commercial's text template management module, allows us to modify a template through the UI.
* I highly recommend you to [check it out](https://commercial.abp.io/modules/Volo.TextTemplateManagement).
## References
* [Text Templating](https://docs.abp.io/en/abp/latest/Text-Templating)
* [Emailing](https://docs.abp.io/en/abp/latest/Emailing)
* [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System)

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/bee.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/db-migrator-1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/db-migrator-2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/db-migrator-3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/email-last.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/embedded-resource-2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/embedded-resource.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/inline-content.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/message.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/settings.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
docs/en/Community-Articles/2020-09-09-Replacing-Email-Template-and-Sending-Emails/template-definitions.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

160
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md

@ -0,0 +1,160 @@
# How to Add Custom Properties to the User Entity
## Introduction
In this step-by-step article, I will explain how you can customize the user entity class, which is available in every web application you create using the ABP framework, according to your needs. When you read this article, you will learn how to override the services of built-in modules, extend the entities, extend data transfer objects and customize the user interface in the applications you develop using the ABP framework.
You can see the screenshots below which we will reach at the end of the article.
![custom-identity-user-list](./custom-identity-user-list.png)
![new-user](./new-user.png)
## Preparing the Project
### Startup template and the initial run
Abp Framework offers startup templates to get into the work faster. We can create a new startup template using Abp CLI:
`abp new CustomizeUserDemo`
> In this article, I will go through the MVC application, but it will work also in the [Angular](https://docs.abp.io/en/abp/latest/Getting-Started?UI=NG&DB=EF&Tiered=No) application.
After the download is finished, we can run **CustomizeUserDemo.DbMigrator** project to create the database migrations and seed the initial data (admin user, role, etc). Then we can run `CustomizeUserDemo.Web` to see that our application is working.
> Default admin username is **admin** and password is **1q2w3E\***
![initial-project](./initial-project.png)
In this article, we will go through a scenario together and find the solutions to our questions through this scenario. However, since the scenario is not a real-life scenario, it may be strange, please don't get too about this issue :)
## Step-1
Add two new properties to the `AppUser` in the Users folder of the **CustomizeUserDemo.Domain** project as follows:
```csharp
public string Title { get; protected set; }
public int Reputation { get; protected set; }
```
## Step-2
Create the Users folder in the **CustomizeUserDemo.Domain.Shared** project, create the class `UserConsts` inside the folder and update the class you created as below:
```csharp
public static class UserConsts
{
public const string TitlePropertyName = "Title";
public const string ReputationPropertyName = "Reputation";
public const int MaxTitleLength = 64;
public const double MaxReputationValue = 1_000;
public const double MinReputationValue = 1;
}
```
## Step-3
Update the `CustomizeUserDemoEfCoreEntityExtensionMappings` class in the **CustomizeUserDemo.EntityFramework** project in the EntityFrameworkCore folder as below:
```csharp
public static class CustomizeUserDemoEfCoreEntityExtensionMappings
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
CustomizeUserDemoGlobalFeatureConfigurator.Configure();
CustomizeUserDemoModuleExtensionConfigurator.Configure();
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Title),
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.IsRequired();
propertyBuilder.HasMaxLength(UserConsts.MaxTitleLength);
}
).MapEfCoreProperty<IdentityUser, int>(
nameof(AppUser.Reputation),
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(UserConsts.MinReputationValue);
}
);
});
}
}
```
This class can be used to map these extra properties to table fields in the database. Please read [this](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities) article to improve your understanding of what we are doing.
So far, we have added our extra features to the `User` entity and matched these features with the `ef core`.
Now we need to add migration to see what has changed in our database. This for, open the Package Manager Console (PMC) under the menu Tools > NuGet Package Manager.
![nuget-package-manager](./nuget-package-manager.png)
Select the **CustomizeUserDemo.EntityFramework.DbMigrations** as the **default project** and execute the following command:
```bash
Add-Migration "Updated-User-Entity"
```
![added-new-migration](./added-new-migration.png)
This will create a new migration class inside the `Migrations` folder of the **CustomizeUserDemo.EntityFrameworkCore.DbMigrations** project.
> If you are using another IDE than the Visual Studio, you can use `dotnet-ef` tool as [documented here](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli#create-a-migration).
Finally, run the **CustomizeUserDemo.DbMigrator** project to update the database.
When we updated the database, you can see that the `Title` and `Reputation` columns are added to the `Users` table.
![user-table](./user-table.png)
## Step-4
Open the `CustomizeUserDemoModuleExtensionConfigurator` in the **CustomizeUserDemo.Domain.Shared** project, and change the contents of the `ConfigureExtraProperties` method as shown below:
```csharp
private static void ConfigureExtraProperties()
{
ObjectExtensionManager.Instance.Modules().ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>(
UserConsts.TitlePropertyName,
options =>
{
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(UserConsts.MaxTitleLength)
);
}
);
user.AddOrUpdateProperty<int>(
UserConsts.ReputationPropertyName,
options =>
{
options.DefaultValue = UserConsts.MinReputationValue;
options.Attributes.Add(
new RangeAttribute(UserConsts.MinReputationValue, UserConsts.MaxReputationValue)
);
}
);
});
});
}
```
That's it. Now let's run the application and look at the Identity user page. You can also try to edit and recreate a record if you want, it will work even though we haven't done anything extra. Here is the magic code behind ABP framework.
If there is a situation you want to add, you can click the contribute button or make a comment. Also, if you like the article, don't forget to share it :)
Happy coding :)

BIN
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/added-new-migration.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/custom-identity-user-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/initial-project.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/new-user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/nuget-package-manager.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/user-table.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

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

@ -162,6 +162,41 @@ This example class inherits from the `IdentityUserManager` [domain service](Doma
Check the [localization system](Localization.md) to learn how to localize the error messages.
### Example: Overriding a Controller
````csharp
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Account;
using Volo.Abp.DependencyInjection;
namespace MyProject.Controllers
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(AccountController))]
public class MyAccountController : AccountController
{
public MyAccountController(IAccountAppService accountAppService)
: base(accountAppService)
{
}
public override async Task SendPasswordResetCodeAsync(
SendPasswordResetCodeDto input)
{
Logger.LogInformation("Your custom logic...");
await base.SendPasswordResetCodeAsync(input);
}
}
}
````
This example replaces the `AccountController` (An API Controller defined in the [Account Module](Modules/Account.md)) and overrides the `SendPasswordResetCodeAsync` method.
**`[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).
### 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.

65
docs/en/Distributed-Event-Bus-Rebus-Integration.md

@ -0,0 +1,65 @@
# Distributed Event Bus Rebus Integration
> This document explains **how to configure the [Rebus](http://mookid.dk/category/rebus/)** as the distributed event bus provider. See the [distributed event bus document](Distributed-Event-Bus.md) to learn how to use the distributed event bus system
## Installation
Use the ABP CLI to add [Volo.Abp.EventBus.Rebus](https://www.nuget.org/packages/Volo.Abp.EventBus.Rebus) NuGet package to your project:
* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before.
* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.EventBus.Rebus` package.
* Run `abp add-package Volo.Abp.EventBus.Rebus` command.
If you want to do it manually, install the [Volo.Abp.EventBus.Rebus](https://www.nuget.org/packages/Volo.Abp.EventBus.Rebus) NuGet package to your project and add `[DependsOn(typeof(AbpEventBusRebusModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project.
## Configuration
You can configure using the standard [configuration system](Configuration.md), like using the [options](Options.md) classes.
### The Options Classes
`AbpRebusEventBusOptions` classe can be used to configure the event bus options for the Rebus.
You can configure this options inside the `PreConfigureServices` of your [module](Module-Development-Basics.md).
**Example: Minimize configuration**
```csharp
PreConfigure<AbpRebusEventBusOptions>(options =>
{
options.InputQueueName = "eventbus";
});
```
Rebus has many options, you can use the `Configurer` property of `AbpRebusEventBusOptions` class to configure.
Default events are **stored in memory**. See the [rebus document](https://github.com/rebus-org/Rebus/wiki/Transport) for more details.
**Example: Configure the store**
````csharp
PreConfigure<AbpRebusEventBusOptions>(options =>
{
options.InputQueueName = "eventbus";
options.Configurer = rebusConfigurer =>
{
rebusConfigurer.Transport(t => t.UseMsmq("eventbus"));
rebusConfigurer.Subscriptions(s => s.UseJsonFile(@"subscriptions.json"));
};
});
````
You can use the `Publish` properpty of `AbpRebusEventBusOptions` class to change the publishing method
**Example: Configure the event publishing**
````csharp
PreConfigure<AbpRebusEventBusOptions>(options =>
{
options.InputQueueName = "eventbus";
options.Publish = async (bus, type, data) =>
{
await bus.Publish(data);
};
});
````

3
docs/en/Distributed-Event-Bus.md

@ -8,7 +8,8 @@ Distributed event bus system provides an **abstraction** that can be implemented
* `LocalDistributedEventBus` is the default implementation that implements the distributed event bus to work as in-process. Yes! The **default implementation works just like the [local event bus](Local-Event-Bus.md)**, if you don't configure a real distributed provider.
* `RabbitMqDistributedEventBus` implements the distributed event bus with the [RabbitMQ](https://www.rabbitmq.com/). See the [RabbitMQ integration document](Distributed-Event-Bus-RabbitMQ-Integration.md) to learn how to configure it.
* `KafkaDistributedEventBus` implements the distributed event bus with the [RabbitMQ](https://kafka.apache.org/). See the [Kafka integration document](Distributed-Event-Bus-Kafka-Integration.md) to learn how to configure it.
* `KafkaDistributedEventBus` implements the distributed event bus with the [Kafka](https://kafka.apache.org/). See the [Kafka integration document](Distributed-Event-Bus-Kafka-Integration.md) to learn how to configure it.
* `RebusDistributedEventBus` implements the distributed event bus with the [Rebus](http://mookid.dk/category/rebus/). See the [Rebus integration document](Distributed-Event-Bus-Rebus-Integration.md) to learn how to configure it.
Using a local event bus as default has a few important advantages. The most important one is that: It allows you to write your code compatible to distributed architecture. You can write a monolithic application now that can be split into microservices later. It is a good practice to communicate between bounded contexts (or between application modules) via distributed events instead of local events.

12
docs/en/Entity-Framework-Core-SQLite.md

@ -23,6 +23,16 @@ Find `UseSqlServer()` calls in your solution, replace with `UseSqlite()`. Check
SQLite connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com]( https://www.connectionstrings.com/sqlite/ ) for details of SQLite connection string options.
An example connection string is
```
{
"ConnectionStrings": {
"Default": "Filename=./MySQLiteDBFile.sqlite"
}
}
```
You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure.
## Re-Generate the Migrations
@ -38,4 +48,4 @@ Run the `.DbMigrator` project to create the database and seed the initial data.
## Run the Application
It is ready. Just run the application and enjoy coding.
It is ready. Just run the application and enjoy coding.

3
docs/en/JavaScript/Dynamic-JavaScript-API-Clients.md

@ -1,3 +0,0 @@
## Dynamic JavaScript API Clients
TODO

99
docs/en/Localization.md

@ -100,8 +100,6 @@ Configure<AbpLocalizationOptions>(options =>
> The [application startup template](Startup-Templates/Application.md) sets `DefaultResourceType` to the localization resource of the application.
See the *Client Side* section below for a use case.
### Short Localization Resource Name
Localization resources are also available in the client (JavaScript) side. So, setting a short name for the localization resource makes it easy to use localization texts. Example:
@ -156,13 +154,13 @@ services.Configure<AbpLocalizationOptions>(options =>
* If an extension file defines the same localized string, it overrides the string.
## Getting Localized Texts
## Getting the Localized Texts
### Server Side
Getting the localized text is pretty standard.
Getting the localized text on the server side is pretty standard.
### Simplest Usage In A Class
#### Simplest Usage In A Class
Just inject the `IStringLocalizer<TResource>` service and use it like shown below:
````C#
public class MyService
@ -183,9 +181,13 @@ public class MyService
##### Format Arguments
Format arguments can be passed after the localization key. If your message is `Hello {0}, welcome!`, then you can pass the `{0}` argument to the localizer like `_localizer["HelloMessage", "John"]`
Format arguments can be passed after the localization key. If your message is `Hello {0}, welcome!`, then you can pass the `{0}` argument to the localizer like `_localizer["HelloMessage", "John"]`.
> Refer to the [Microsoft's localization documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) for details about using the localization.
#### Simplest Usage In A Razor View/Page
### Using In A Razor View/Page
Use `IHtmlLocalizer<T>` in razor views/pages;
````c#
@inject IHtmlLocalizer<TestResource> Localizer
@ -193,54 +195,59 @@ Format arguments can be passed after the localization key. If your message is `H
<h1>@Localizer["HelloWorld"]</h1>
````
Refer to the [Microsoft's localization documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) for details about using localization on the server side.
### Client Side
ABP provides JavaScript services to use the same localized texts in the client side.
#### getResource
`abp.localization.getResource` function is used to get a localization resource:
````js
var testResource = abp.localization.getResource('Test');
````
Then you can localize a string based on this resource:
### Special Base Classes
````js
var str = testResource('HelloWorld');
````
Some ABP Framework base classes provide a `L` property to use the localizer even easier.
#### localize
**Example: Localize a text in an application service method**
`abp.localization.localize` function is a shortcut where you can both specify the text name and the resource name:
```csharp
using System.Threading.Tasks;
using MyProject.Localization;
using Volo.Abp.Application.Services;
````js
var str = abp.localization.localize('HelloWorld', 'Test');
````
namespace MyProject
{
public class TestAppService : ApplicationService
{
public TestAppService()
{
LocalizationResource = typeof(MyProjectResource);
}
`HelloWorld` is the text to localize, where `Test` is the localization resource name here.
public async Task DoIt()
{
var str = L["HelloWorld"];
}
}
}
```
If you don't specify the localization resource name, it uses the default localization resource defined on the `AbpLocalizationOptions` (see the *Default Resource* section above). Example:
When you set the `LocalizationResource` in the constructor, the `ApplicationService` class uses that resource type when you use the `L` property, just like in the `DoIt()` method.
````js
var str = abp.localization.localize('HelloWorld'); //uses the default resource
````
Setting `LocalizationResource` in every application service can be tedious. You can create an abstract base application service class, set it there and derive your application services from that base class. This is already implemented when you create a new project with the [startup templates](Startup-Templates/Application.md). So, you can simply inherit from the base class directly use the `L` property:
##### Format Arguments
```csharp
using System.Threading.Tasks;
If your localized string contains arguments, like `Hello {0}, welcome!`, you can pass arguments to the localization methods. Examples:
namespace MyProject
{
public class TestAppService : MyProjectAppService
{
public async Task DoIt()
{
var str = L["HelloWorld"];
}
}
}
```
````js
var str1 = abp.localization.getResource('Test')('HelloWelcomeMessage', 'John');
var str2 = abp.localization.localize('HelloWorld', 'Test', 'John');
````
The `L` property is also available for some other base classes like `AbpController` and `AbpPageModel`.
Both of the samples above produce the output `Hello John, welcome!`.
## The Client Side
## See Also
See the following documents to learn how to reuse the same localization texts in the JavaScript side;
* [Localization in Angular UI](UI/Angular/Localization.md)
* [Forms & Validation](UI/AspNetCore/Forms-Validation.md) for the ASP.NET Core MVC / Razor Pages UI
* [Localization for the MVC / Razor Pages UI](UI/AspNetCore/JavaScript-API/Localization.md)
* [Localization for the Blazor UI](UI/Blazor/Localization.md)
* [Localization for the Angular UI](UI/Angular/Localization.md)

13
docs/en/Migration-Guides/BlazorUI-3_3.md

@ -0,0 +1,13 @@
# Migration Guide for the Blazor UI from the v3.2 to the v3.3
## Startup Template Changes
* Remove `Volo.Abp.Account.Blazor` NuGet package from your `.Blazor.csproj` and add `Volo.Abp.TenantManagement.Blazor` NuGet package.
* Remove the ``typeof(AbpAccountBlazorModule)`` from the dependency list of *YourProjectBlazorModule* class and add the `typeof(AbpTenantManagementBlazorModule)`.
* Add `@using Volo.Abp.BlazoriseUI` and `@using Volo.Abp.BlazoriseUI.Components` into the `_Imports.razor` file.
* Remove the `div` with `id="blazor-error-ui"` (with its contents) from the `wwwroot/index.html ` file, since the ABP Framework now shows error messages as a better message box.
## BlazoriseCrudPageBase to AbpCrudPageBase
Renamed `BlazoriseCrudPageBase` to `AbpCrudPageBase`. Just update the usages. It also has some changes, you may need to update method calls/usages manually.

2
docs/en/Redis-Cache.md

@ -25,9 +25,11 @@ Volo.Abp.Caching.StackExchangeRedis package automatically gets the redis [config
````js
"Redis": {
"IsEnabled": "true",
"Configuration": "127.0.0.1"
}
````
The setting `IsEnabled` is optional and will be considered `true` if it is not set.
Alternatively you can configure the standard [RedisCacheOptions](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.stackexchangeredis.rediscacheoptions) [options](Options.md) class in the `ConfigureServices` method of your [module](Module-Development-Basics.md):

49
docs/en/Repositories.md

@ -142,7 +142,7 @@ var people = _personRepository
You normally want to use `.ToListAsync()`, `.CountAsync()`... instead, to be able to write a **truly async code**.
However, you see that you can't use these async extension methods in your application or domain layer when you create a new project using the standard [application startup template](Startup-Templates/Application.md), because;
However, you see that you can't use all the async extension methods in your application or domain layer when you create a new project using the standard [application startup template](Startup-Templates/Application.md), because;
* These async methods **are not standard LINQ methods** and they are defined in the [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore) NuGet package.
* The standard template **doesn't have a reference** to the EF Core package from the domain and application layers, to be independent from the database provider.
@ -151,9 +151,9 @@ Based on your requirements and development model, you have the following options
> Using async methods is strongly suggested! Don't use sync LINQ methods while executing database queries to be able to develop a scalable application.
### Option-1: Reference to the EF Core
### Option-1: Reference to the Database Provider Package
The easiest solution is to directly add the EF Core package from the project you want to use these async methods.
**The easiest solution** is to directly add the EF Core package from the project you want to use these async methods.
> Add the [Volo.Abp.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore) NuGet package to your project, which indirectly reference to the EF Core package. This ensures that you use the correct version of the EF Core compatible to the rest of your application.
@ -183,14 +183,34 @@ var people = ((IMongoQueryable<Person>)_personRepository
.ToListAsync();
````
### Option-2: Custom Repository Methods
### Option-2: Use the IRepository Async Extension Methods
You can always create custom repository methods and use the database provider specific APIs, like async extension methods here. See [EF Core](Entity-Framework-Core.md) or [MongoDb](MongoDB.md) document for more info about the custom repositories.
ABP Framework provides async extension methods for the repositories, just similar to async LINQ extension methods.
This method is suggested;
**Example: Use `CountAsync` and `FirstOrDefaultAsync` methods on the repositories**
* If you want to **completely isolate** your domain & application layers from the database provider.
* If you develop a **reusable [application module](Modules/Index.md)** and don't want to force to a specific database provider, which should be done as a [best practice](Best-Practices/Index.md).
````csharp
var countAll = await _personRepository
.CountAsync();
var count = await _personRepository
.CountAsync(x => x.Name.StartsWith("A"));
var book1984 = await _bookRepository
.FirstOrDefaultAsync(x => x.Name == "John");
````
The standard LINQ extension methods are supported: *AllAsync, AnyAsync, AverageAsync, ContainsAsync, CountAsync, FirstAsync, FirstOrDefaultAsync, LastAsync, LastOrDefaultAsync, LongCountAsync, MaxAsync, MinAsync, SingleAsync, SingleOrDefaultAsync, SumAsync, ToArrayAsync, ToListAsync*.
This approach still **has a limitation**. You need to call the extension method directly on the repository object. For example, the below usage is **not supported**:
```csharp
var count = await _bookRepository.Where(x => x.Name.Contains("A")).CountAsync();
```
This is because the object returned from the `Where` method is not a repository object, it is a standard `IQueryable` interface. See the other options for such cases.
This method is suggested **wherever possible**.
### Option-3: IAsyncQueryableExecuter
@ -245,6 +265,17 @@ ABP Framework executes the query asynchronously using the actual database provid
This method is suggested;
* If you want to develop your application code **without depending** on the database provider.
* If you are building a **reusable library** that doesn't have a database provider integration package, but needs to execute an `IQueryable<T>` object in some case.
For example, ABP Framework uses the `IAsyncQueryableExecuter` in the `CrudAppService` base class (see the [application services](Application-Services.md) document).
For example, ABP Framework uses the `IAsyncQueryableExecuter` in the `CrudAppService` base class (see the [application services](Application-Services.md) document).
### Option-4: Custom Repository Methods
You can always create custom repository methods and use the database provider specific APIs, like async extension methods here. See [EF Core](Entity-Framework-Core.md) or [MongoDb](MongoDB.md) document for more info about the custom repositories.
This method is suggested;
* If you want to **completely isolate** your domain & application layers from the database provider.
* If you develop a **reusable [application module](Modules/Index.md)** and don't want to force to a specific database provider, which should be done as a [best practice](Best-Practices/Index.md).

23
docs/en/Settings.md

@ -2,7 +2,7 @@
[Configuration system](Configuration.md) is a good way to configure the application on startup. In addition to the configurations, ABP provides another way to set and get some application settings.
A setting is a name-value pair stored in a dynamic data source, generally in a database. Setting system is extensible and there are pre-built provides for a user, a tenant, global and default.
A setting is a name-value pair stored in a dynamic data source, generally in a database. Setting system is extensible and there are pre-built providers for a user, a tenant, global and default.
## Defining Settings
@ -67,7 +67,7 @@ public class MySettingDefinitionProvider : SettingDefinitionProvider
> Using constants for the setting names is a good practice and ABP packages do it. `Abp.Mailing.Smtp.Host` setting name is a constant defined by the `EmailSettingNames` class (in the `Volo.Abp.Emailing` namespace).
## Reading Setting Values
## Reading the Setting Values
### ISettingProvider
@ -108,24 +108,15 @@ public class MyService
}
````
> `ISettingProvider` is a very common service and some base classes (like `IApplicationService`) already property-inject it. You can directly use the `SettingProvider` in such cases.
> `ISettingProvider` is a very common service and some base classes (like `IApplicationService`) already property-inject it. You can directly use the `SettingProvider` property in such cases.
### Reading Setting Values on the Client Side
If a setting is allowed to be visible on the client side, current value of the setting can also be read from the JavaScript code. Examples:
If a setting is allowed to be visible on the client side, current value of the setting can also be read from the client code. See the following documents to understand how to get the setting values in different UI types;
````js
//Gets a value as string.
var language = abp.setting.get('Abp.Localization.DefaultLanguage');
//Gets an integer value.
var requiredLength = abp.setting.getInt('Abp.Identity.Password.RequiredLength');
//Gets a boolean value.
var requireDigit = abp.setting.getBoolean('Abp.Identity.Password.RequireDigit');
````
In addition, use `abp.setting.values` to get a dictionary of all the setting values.
* [MVC / Razor Pages](UI/AspNetCore/JavaScript-API/Settings.md)
* [Angular](UI/Angular/Settings.md)
* [Blazor](UI/Blazor/Settings.md)
## Setting Value Providers

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

@ -38,7 +38,7 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
We have created `Book` and `Author` functionalities for the book store application. However, currently there is no relation between these entities.
In this tutorial, we will establish a **1 to N** relation between the `Book` and the `Author`.
In this tutorial, we will establish a **1 to N** relation between the `Author` and the `Book` entities.
## Add Relation to The Book Entity

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

@ -583,7 +583,7 @@ When you click to the Books menu item under the Book Store parent, you are being
We will use the [Blazorise library](https://blazorise.com/) as the UI component kit. It is a very powerful library that supports major HTML/CSS frameworks, including the Bootstrap.
ABP Framework provides a generic base class, `BlazoriseCrudPageBase<...>`, to create CRUD style pages. This base class is compatible to the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `BlazoriseCrudPageBase` to automate the standard CRUD stuff.
ABP Framework provides a generic base class, `AbpCrudPageBase<...>`, to create CRUD style pages. This base class is compatible to the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `AbpCrudPageBase` to automate the code behind for the standard CRUD stuff.
Open the `Books.razor` and replace the content as the following:
@ -595,7 +595,7 @@ Open the `Books.razor` and replace the content as the following:
@using Acme.BookStore.Localization
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<BookStoreResource> L
@inherits BlazoriseCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
@inherits AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
<Card>
<CardHeader>
@ -645,15 +645,15 @@ Open the `Books.razor` and replace the content as the following:
> If you see some syntax errors, you can ignore them if your application properly built and run. Visual Studio still has some bugs with Blazor.
* Inherited from the `BlazoriseCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>` which implements all the CRUD details for us.
* Inherited from the `AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>` which implements all the CRUD details for us.
* `Entities`, `TotalCount`, `PageSize`, `OnDataGridReadAsync` are defined in the base blass.
* Injected `IStringLocalizer<BookStoreResource>` (as `L` object) and used for localization.
While the code above pretty easy to understand, you can check the Blazorise [Card](https://blazorise.com/docs/components/card/) and [DataGrid](https://blazorise.com/docs/extensions/datagrid/) documents to understand them better.
#### About the BlazoriseCrudPageBase
#### About the AbpCrudPageBase
We will continue to benefit from the `BlazoriseCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call server side HTTP APIs in your Blazor applications.
We will continue to benefit from the `AbpCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call server side HTTP APIs in your Blazor applications.
## Run the Final Application

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

@ -1163,7 +1163,7 @@ Clicking the "Delete" action calls the `delete` method which then shows a confir
## Creating a New Book
In this section, you will learn how to create a new modal dialog form to create a new book. Since we've inherited from the `BlazoriseCrudPage`, we only need to develop the view part.
In this section, you will learn how to create a new modal dialog form to create a new book. Since we've inherited from the `AbpCrudPageBase`, we only need to develop the view part.
### Add "New Button" Button
@ -1328,7 +1328,7 @@ We can now define a modal to edit the book. Add the following code to the end of
### AutoMapper Configuration
The base `BlazoriseCrudPage` uses the [object to object mapping](../Object-To-Object-Mapping.md) system to convert an incoming `BookDto` object to a `CreateUpdateBookDto` object. So, we need to define the mapping.
The base `AbpCrudPageBase` uses the [object to object mapping](../Object-To-Object-Mapping.md) system to convert an incoming `BookDto` object to a `CreateUpdateBookDto` object. So, we need to define the mapping.
Open the `BookStoreBlazorAutoMapperProfile` inside the `Acme.BookStore.Blazor` project and change the content as the following:
@ -1382,7 +1382,7 @@ Here the complete code to create the book management CRUD page, that has been de
@using Acme.BookStore.Localization
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<BookStoreResource> L
@inherits BlazoriseCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
@inherits AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
<Card>
<CardHeader>
@ -1392,8 +1392,10 @@ Here the complete code to create the book management CRUD page, that has been de
</Column>
<Column ColumnSize="ColumnSize.Is6">
<Paragraph Alignment="TextAlignment.Right">
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">@L["NewBook"]</Button>
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">
@L["NewBook"]
</Button>
</Paragraph>
</Column>
</Row>
@ -1406,10 +1408,10 @@ Here the complete code to create the book management CRUD page, that has been de
ShowPager="true"
PageSize="PageSize">
<DataGridColumns>
<DataGridColumn Width="150px"
<DataGridColumn Width="150px"
TItem="BookDto"
Field="@nameof(BookDto.Id)"
Sortable="false"
Field="@nameof(BookDto.Id)"
Sortable="false"
Caption="@L["Actions"]">
<DisplayTemplate>
<Dropdown>
@ -1434,7 +1436,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"

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

@ -75,9 +75,12 @@ If you had created a data seed contributor as described in the [first part](Part
Add a new test class, named `BookAppService_Tests` in the `Books` namespace (folder) of the `Acme.BookStore.Application.Tests` project:
````csharp
using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit;
namespace Acme.BookStore.Books

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

@ -460,7 +460,7 @@ Open the `/src/app/book/book.component.html` file and replace the edit and delet
### Authorize the Razor Component
Open the `/Pages/Books.razor` file in the `Acme.BookStore.Blazor` project and add an `Authorize` attribute just after the `@page` directive, as shown below:
Open the `/Pages/Books.razor` file in the `Acme.BookStore.Blazor` project and add an `Authorize` attribute just after the `@page` directive and the following namespace imports (`@using` lines), as shown below:
````html
@page "/books"
@ -518,7 +518,9 @@ Wrap the *New Book* button by an `if` block as shown below:
@if (canCreateBook)
{
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">@L["NewBook"]</Button>
Clicked="OpenCreateModalAsync">
@L["NewBook"]
</Button>
}
````
@ -545,7 +547,7 @@ As similar to the *New Book* button, we can use `if` blocks to conditionally sho
You can run and test the permissions. Remove a book related permission from the admin role to see the related button/action disappears from the UI.
However, ABP Framework caches the permissions of the current user in the client side. So, when you change a permission for yourself, you need to manually **refresh the page** to take the effect. If you don't refresh and try to use the prohibited action you get an HTTP 403 (forbidden) response from the server.
**ABP Framework caches the permissions** of the current user in the client side. So, when you change a permission for yourself, you need to manually **refresh the page** to take the effect. If you don't refresh and try to use the prohibited action you get an HTTP 403 (forbidden) response from the server.
> Changing a permission for a role or user immediately available on the server side. So, this cache system doesn't cause any security problem.

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

@ -74,6 +74,10 @@ Open the **Package Manager Console** on Visual Studio and ensure that the **Defa
Run the following command to create a new database migration:
````bash
Add-Migration "Added_Authors"
````
![bookstore-add-migration-authors](images/bookstore-add-migration-authors.png)
This will create a new migration class. Then run the `Update-Database` command to create the table on the database.

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

@ -1177,6 +1177,20 @@ We should complete the localizations we've used above. Open the `en.json` file u
"NewAuthor": "New author"
````
### Run the Application
Run and login to the application. **If you don't see the Authors menu item under the Book Store menu, that means you don't have the permission yet.** Go to the `identity/roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**:
![bookstore-author-permissions](images/bookstore-author-permissions.png)
As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**:
![bookstore-authors-page](images/bookstore-authors-blazor-ui.png)
That's all! This is a fully working CRUD page, you can create, edit and delete the authors.
> **Tip**: If you run the `.DbMigrator` console application after defining a new permission, it automatically grants these new permissions to the admin role and you don't need to manually grant the permissions yourself.
{{end}}
## The Next Part

BIN
docs/en/Tutorials/images/bookstore-authors-blazor-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

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

@ -219,7 +219,18 @@ If you see an error like this, you should pass the `cultureNameLocaleFileMap` pr
See [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales).
## Adding new culture
```js
//app.module.ts
import { storeLocaleData } from '@abp/ng.core';
import(
/* webpackChunkName: "_locale-your-locale-js"*/
/* webpackMode: "eager" */
'@angular/common/locales/your-locale.js'
).then(m => storeLocaleData(m.default, 'your-locale'));
```
## See Also
* [Localization in ASP.NET Core](../../Localization.md)

3
docs/en/UI/Angular/Settings.md

@ -0,0 +1,3 @@
# Angular UI: Settings
> This document explains how to get setting values in an Angular application. See the [settings document](../../Settings.md) to learn the setting system.

96
docs/en/UI/Angular/Toaster-Service.md

@ -85,7 +85,103 @@ The all open toasts can be removed manually via the `clear` method:
```js
this.toaster.clear();
```
## Replacing ToasterService with 3rd party toaster libraries
If you want the ABP Framework to utilize 3rd party libraries for the toasters instead of the built-in one, you can provide a service that implements `Toaster.Service` interface, and provide it as follows (ngx-toastr library used in example):
> You can use *LocalizationService* for toaster messages translations.
```js
// your-custom-toaster.service.ts
import { Injectable } from '@angular/core';
import { Config, LocalizationService } from '@abp/ng.core';
import { Toaster } from '@abp/ng.theme.shared';
import { ToastrService } from 'ngx-toastr';
@Injectable()
export class CustomToasterService implements Toaster.Service {
constructor(private toastr: ToastrService, private localizationService: LocalizationService) {}
error(
message: Config.LocalizationParam,
title?: Config.LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
) {
return this.show(message, title, 'error', options);
}
clear(): void {
this.toastr.clear();
}
info(
message: Config.LocalizationParam,
title: Config.LocalizationParam | undefined,
options: Partial<Toaster.ToastOptions> | undefined,
): Toaster.ToasterId {
return this.show(message, title, 'info', options);
}
remove(id: number): void {
this.toastr.remove(id);
}
show(
message: Config.LocalizationParam,
title: Config.LocalizationParam,
severity: Toaster.Severity,
options: Partial<Toaster.ToastOptions>,
): Toaster.ToasterId {
const translatedMessage = this.localizationService.instant(message);
const translatedTitle = this.localizationService.instant(title);
const toasterOptions = {
positionClass: 'toast-bottom-right',
tapToDismiss: options.tapToDismiss,
...(options.sticky && {
extendedTimeOut: 0,
timeOut: 0,
}),
};
const activeToast = this.toastr.show(
translatedMessage,
translatedTitle,
toasterOptions,
`toast-${severity}`,
);
return activeToast.toastId;
}
success(
message: Config.LocalizationParam,
title: Config.LocalizationParam | undefined,
options: Partial<Toaster.ToastOptions> | undefined,
): Toaster.ToasterId {
return this.show(message, title, 'success', options);
}
warn(
message: Config.LocalizationParam,
title: Config.LocalizationParam | undefined,
options: Partial<Toaster.ToastOptions> | undefined,
): Toaster.ToasterId {
return this.show(message, title, 'warning', options);
}
}
```
```js
// app.module.ts
import { ToasterService } from '@abp/ng.theme.shared';
@NgModule({
providers: [
// ...
{
provide: ToasterService,
useClass: CustomToasterService,
},
]
})
```
## API
### success

3
docs/en/UI/AspNetCore/Client-Side-Package-Management.md

@ -94,7 +94,8 @@ An example mapping configuration is shown below:
mappings: {
"@node_modules/bootstrap/dist/css/bootstrap.css": "@libs/bootstrap/css/",
"@node_modules/bootstrap/dist/js/bootstrap.bundle.js": "@libs/bootstrap/js/",
"@node_modules/bootstrap-datepicker/dist/locales/*.*": "@libs/bootstrap-datepicker/locales/"
"@node_modules/bootstrap-datepicker/dist/locales/*.*": "@libs/bootstrap-datepicker/locales/",
"@node_modules/bootstrap-v4-rtl/dist/**/*": "@libs/bootstrap-v4-rtl/dist/"
}
````

4
docs/en/UI/AspNetCore/Customization-User-Interface.md

@ -376,7 +376,7 @@ Assume that you need to add the Google Analytics script to the layout (that will
![bookstore-google-analytics-view-component](../../images/bookstore-google-analytics-view-component.png)
**NotificationViewComponent.cs**
**GoogleAnalyticsViewComponent.cs**
````csharp
public class GoogleAnalyticsViewComponent : AbpViewComponent
@ -441,7 +441,7 @@ Layout system allows themes to define standard, named layouts and allows any pag
* "**Account**": This layout is used by login, register and other similar pages. It is used for the pages under the `/Pages/Account` folder by default.
* "**Empty**": Empty and minimal layout.
These names are defined in the `StandardLayouts` class as constants. You can definitely create your own layouts, but these are standard layout names and implemented by all the themes out of the box.
These names are defined in the `StandardLayouts` class as constants. You can definitely create your own layouts, but these are the standard layout names and implemented by all the themes out of the box.
#### Layout Location

150
docs/en/UI/AspNetCore/JavaScript-API/Ajax.md

@ -0,0 +1,150 @@
# ASP.NET Core MVC / Razor Pages UI JavaScript AJAX API
`abp.ajax` API provides a convenient way of performing AJAX calls to the server. It internally uses JQuery's `$.ajax`, but automates some common tasks for you;
* Automatically **handles & localize the errors** and informs the user (using the [abp.message](Message.md)). So you typically don't care about errors.
* Automatically adds **anti forgery** token to the HTTP header to satisfy CSRF protection validation on the server side.
* Automatically sets **default options** and allows to configure the defaults in a single place.
* Can **block** a UI part (or the full page) during the AJAX operation.
* Allows to fully customize any AJAX call, by using the standard `$.ajax` **options**.
> While `abp.ajax` makes the AJAX call pretty easier, you typically will use the [Dynamic JavaScript Client Proxy](Dynamic-JavaScript-Client-Proxies.md) system to perform calls to your server side HTTP APIs. `abp.ajax` can be used when you need to perform low level AJAX operations.
## Basic Usage
`abp.ajax` accepts an options object that is accepted by the standard [$.ajax](https://api.jquery.com/jquery.ajax/#jQuery-ajax-settings). All the standard options are valid. It returns a [promise](https://api.jquery.com/category/deferred-object/) as the return value.
**Example: Get the list of users**
````js
abp.ajax({
type: 'GET',
url: '/api/identity/users'
}).then(function(result){
console.log(result);
});
````
This command logs the list of users to the console, if you've **logged in** to the application and have [permission](../../../Authorization.md) for the user management page of the [Identity Module](../../../Modules/Identity.md).
## Error Handling
The example AJAX call above shows an **error message** if you haven't login to the application or you don't have the necessary permissions to perform this request:
![ajax-error](../../../images/ajax-error.png)
All kinds of errors are automatically handled by `abp.ajax`, unless you want to disable it.
### Standard Error Response
`abp.ajax` is compatible with the [exception handling system](../../../Exception-Handling.md) of the ABP Framework and it properly handles the standard error format returned from the server. A typical error message is a JSON as like below:
````json
{
"error": {
"code": "App:010042",
"message": "This topic is locked and can not add a new message",
"details": "A more detailed info about the error..."
}
}
````
The error message is directly shown to the user, using the `message` and `details` properties.
### Non-Standard Error Response & HTTP Status Codes
It also handles errors even if the standard error format was not sent by the server. This can be case if you bypass the ABP exception handling system and manually build the HTTP response on the server. In that case, **HTTP status codes** are considered.
The following HTTP Status Codes are pre-defined;
* **401**: Shows an error message like "*You should be authenticated (sign in) in order to perform this operation*". When the users click the OK button, they are redirected to the home page of the application to make them login again.
* **403**: Shows an error message like "*You are not allowed to perform this operation*".
* **404**: Shows an error message like "*The resource requested could not found on the server*".
* **Others**: Shows a generic error message like "*An error has occurred. Error detail not sent by server*".
All these messages are localized based on the current user's language.
### Manually Handling the Errors
Since `abp.ajax` returns a promise, you can always chain a `.cactch(...)` call to register a callback that is executed if the AJAX request fails.
**Example: Show an alert if the AJAX request fails**
````js
abp.ajax({
type: 'GET',
url: '/api/identity/users'
}).then(function(result){
console.log(result);
}).catch(function(){
alert("request failed :(");
});
````
While your callback is fired, ABP still handles the error itself. If you want to disable automatic error handling, pass `abpHandleError: false` the the `abp.ajax` options.
**Example: Disable the auto error handling**
````js
abp.ajax({
type: 'GET',
url: '/api/identity/users',
abpHandleError: false //DISABLE AUTO ERROR HANDLING
}).then(function(result){
console.log(result);
}).catch(function(){
alert("request failed :(");
});
````
If you set `abpHandleError: false` and don't catch the error yourself, then the error will be hidden and the request silently fails. `abp.ajax` still logs the error to the browser console (see the *Configuration* section to override it).
## Configuration
`abp.ajax` has a **global configuration** that you can customize based on your requirements.
### Default AJAX Options
`abp.ajax.defaultOpts` object is used to configure default options used while performing an AJAX call, unless you override them. Default value of this object is shown below:
````js
{
dataType: 'json',
type: 'POST',
contentType: 'application/json',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
}
````
So, if you want to change the default request type, you can do it as shown below:
````js
abp.ajax.defaultOpts.type = 'GET';
````
Write this code before all of your JavaScript code. You typically want to place such a configuration into a separate JavaScript file and add it to the layout using the global [bundle](../Bundling-Minification.md).
### Log/Show Errors
The following functions can be overridden to customize the logging and showing the error messages:
* `abp.ajax.logError` function logs errors using the [abp.log.error(...)](Logging.md) by default.
* `abp.ajax.showError` function shows the error message using the [abp.message.error(...)](Message.md) by default.
* `abp.ajax.handleErrorStatusCode` handles different HTTP status codes and shows different messages based on the code.
* `abp.ajax.handleAbpErrorResponse` handles the errors sent with the standard ABP error format.
* `abp.ajax.handleNonAbpErrorResponse` handles the non-standard error responses.
* `abp.ajax.handleUnAuthorizedRequest` handles responses with `401` status code and redirect users to the home page of the application.
**Example: Override the `logError` function**
````js
abp.ajax.logError = function(error) {
//...
}
````
### Other Options
* `abp.ajax.ajaxSendHandler` function is used to intercept the AJAX requests and add antiforgery token to the HTTP header. Note that this works for all AJAX requests, even if you don't use the `abp.ajax`.

24
docs/en/UI/AspNetCore/JavaScript-API/Auth.md

@ -0,0 +1,24 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Auth API
Auth API allows you to check permissions (policies) for the current user in the client side. In this way, you can conditionally show/hide UI parts or perform your client side logic based on the current permissions.
> This document only explains the JavaScript API. See the [authorization document](../../../Authorization.md) to understand the ABP authorization & permission system.
## Basic Usage
`abp.auth.isGranted(...)` function is used to check if a permission/policy has granted or not:
````js
if (abp.auth.isGranted('DeleteUsers')) {
//TODO: Delete the user
} else {
alert("You don't have permission to delete a user!");
}
````
## Other Fields & Functions
* ` abp.auth.isAnyGranted(...)`: Gets one or more permission/policy names and returns `true` if at least one of them has granted.
* `abp.auth.areAllGranted(...)`: Gets one or more permission/policy names and returns `true` if all of them of them have granted.
* `abp.auth.policies`: This is an object where its keys are the permission/policy names. You can find all permission/policy names here.
* `abp.auth.grantedPolicies`: This is an object where its keys are the permission/policy names. You can find the granted permission/policy names here.

56
docs/en/UI/AspNetCore/JavaScript-API/Block-Busy.md

@ -0,0 +1,56 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript UI Block/Busy API
UI Block API disables (blocks) the page or a part of the page.
## Basic Usage
**Example: Block (disable) the complete page**
````js
abp.ui.block();
````
**Example: Block (disable) an HTML element**
````js
abp.ui.block('#MyContainer');
````
**Example: Enables the previously blocked element or page:**
````js
abp.ui.unblock();
````
## Options
`abp.ui.block()` method can get an options object which may contain the following fields:
* `elm`: An optional selector to find the element to be blocked (e.g. `#MyContainerId`). If not provided, the entire page is blocked. The selector can also be directly passed to the `block()` method as shown above.
* `busy`: Set to `true` to show a progress indicator on the blocked area.
* `promise`: A promise object with `always` or `finally` callbacks. This can be helpful if you want to automatically unblock the blocked area when a deferred operation completes.
**Example: Block an element with busy indicator**
````js
abp.ui.block({
elm: '#MySection',
busy: true
});
````
The resulting UI will look like below:
![ui-busy](../../../images/ui-busy.png)
## setBusy
`abp.ui.setBusy(...)` and `abp.ui.clearBusy()` are shortcut functions if you want to use the block with `busy` option.
**Example: Block with busy**
````js
abp.ui.setBusy('#MySection');
````
Then you can use `abp.ui.clearBusy();` to re-enable the busy area/page.

49
docs/en/UI/AspNetCore/JavaScript-API/CurrentUser.md

@ -0,0 +1,49 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript CurrentUser API
`abp.currentUser` is an object that contains information about the current user of the application.
> This document only explains the JavaScript API. See the [CurrentUser document](../../../CurrentUser.md) to get information about the current user in the server side.
## Authenticated User
If the user was authenticated, this object will be something like below:
````js
{
isAuthenticated: true,
id: "34f1f4a7-13cc-4b91-84d1-b91c87afa95f",
tenantId: null,
userName: "john",
name: "John",
surName: "Nash",
email: "john.nash@abp.io",
emailVerified: true,
phoneNumber: null,
phoneNumberVerified: false,
roles: ["moderator","supporter"]
}
````
So, `abp.currentUser.userName` returns `john` in this case.
## Anonymous User
If the user was not authenticated, this object will be something like below:
````js
{
isAuthenticated: false,
id: null,
tenantId: null,
userName: null,
name: null,
surName: null,
email: null,
emailVerified: false,
phoneNumber: null,
phoneNumberVerified: false,
roles: []
}
````
You can check `abp.currentUser.isAuthenticated` to understand if the use was authenticated or not.

117
docs/en/UI/AspNetCore/JavaScript-API/DOM.md

@ -0,0 +1,117 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript DOM API
`abp.dom` (Document Object Model) provides events that you can subscribe to get notified when elements dynamically added to and removed from the page (DOM).
It is especially helpful if you want to initialize the new loaded elements. This is generally needed when you dynamically add elements to DOM (for example, get some HTML elements via AJAX) after page initialization.
> ABP uses the [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to observe the changes made on the DOM.
## Node Events
### onNodeAdded
This event is triggered when an element is added to the DOM. Example:
````js
abp.dom.onNodeAdded(function(args){
console.log(args.$el);
});
````
`args` object has the following fields;
* `$el`: The JQuery selection to get the new element inserted to the DOM.
### onNodeRemoved
This event is triggered when an element is removed from the DOM. Example:
````js
abp.dom.onNodeRemoved(function(args){
console.log(args.$el);
});
````
`args` object has the following fields;
* `$el`: The JQuery selection to get the element removed from the DOM.
## Pre-Build Initializers
ABP Framework is using the DOM events to initialize some kind of HTML elements when they are added to the DOM after than the page was already initialized.
> Note that the same initializers also work if these elements were already included in the initial DOM. So, whether they are initially or lazy loaded, they work as expected.
### Form Initializer
The Form initializer (defined as `abp.dom.initializers.initializeForms`) initializes the lazy loaded forms;
* Automatically enabled the `unobtrusive` validation on the form.
* Can automatically show a confirmation message when you submit the form. To enable this feature, just add `data-confirm` attribute with a message (like `data-confirm="Are you sure?"`) to the `form` element.
* If the `form` element has `data-ajaxForm="true"` attribute, then automatically calls the `.abpAjaxForm()` on the `form` element, to make the form posted via AJAX.
See the [Forms & Validation](../Forms-Validation.md) document for more.
### Script Initializer
Script initializer (`abp.dom.initializers.initializeScript`) can execute a JavaScript code for a DOM element.
**Example: Lazy load a component and execute some code when the element has loaded**
Assume that you've a container to load the element inside:
````html
<div id="LazyComponent"></div>
````
And this is the component that will be loaded via AJAX from the server and inserted into the container:
````html
<div data-script-class="MyCustomClass">
<p>Sample message</p>
</div>
````
`data-script-class="MyCustomClass"` indicates the JavaScript class that will be used to perform some logic on this element:
`MyCustomClass` is a global object defined as shown below:
````js
MyCustomClass = function(){
function initDom($el){
$el.css('color', 'red');
}
return {
initDom: initDom
}
};
````
`initDom` is the function that is called by the ABP Framework. The `$el` argument is the loaded HTML element as a JQuery selection.
Finally, you can load the component inside the container after an AJAX call:
````js
$(function () {
setTimeout(function(){
$.get('/get-my-element').then(function(response){
$('#LazyComponent').html(response);
});
}, 2000);
});
````
Script Initialization system is especially helpful if you don't know how and when the component will be loaded into the DOM. This can be possible if you've developed a reusable UI component in a library and you want the application developer shouldn't care how to initialize the component in different use cases.
> Script initialization doesn't work if the component was loaded in the initial DOM. In this case, you are responsible to initialize it.
### Other Initializers
The following Bootstrap components and libraries are automatically initialized when they are added to the DOM:
* Tooltip
* Popover
* Timeage

3
docs/en/UI/AspNetCore/JavaScript-API/Dynamic-JavaScript-Client-Proxies.md

@ -0,0 +1,3 @@
# ASP.NET Core MVC / Razor Pages UI: Dynamic JavaScript Client Proxies
TODO

88
docs/en/UI/AspNetCore/JavaScript-API/Events.md

@ -0,0 +1,88 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Events API
`abp.event` object is a simple service that is used to publish and subscribe to global events **in the browser**.
> This API is not related to server side local or distributed events. It works in the browser boundaries to make the UI components (code parts) communicate in a loosely coupled way.
## Basic Usage
### Publishing Events
Use `abp.event.trigger` to publish events.
**Example: Publish a *Basket Updated* event**
````js
abp.event.trigger('basketUpdated');
````
This will trigger all the subscribed callbacks.
### Subscribing to the Events
Use `abp.event.on` to subscribe to events.
**Example: Consume the *Basket Updated* event**
````js
abp.event.on('basketUpdated', function() {
console.log('Handled the basketUpdated event...');
});
````
You start to get events after you subscribe to the event.
### Unsubscribing from the Events
If you need to unsubscribe from a pre-subscribed event, you can use the `abp.event.off(eventName, callback)` function. In this case, you have the callback as a separate function declaration.
**Example: Subscribe & Unsubscribe**
````js
function onBasketUpdated() {
console.log('Handled the basketUpdated event...');
}
//Subscribe
abp.event.on('basketUpdated', onBasketUpdated);
//Unsubscribe
abp.event.off('basketUpdated', onBasketUpdated);
````
You don't get events after you unsubscribe from the event.
## Event Arguments
You can pass arguments (of any count) to the `trigger` method and get them in the subscription callback.
**Example: Add the basket as the event argument**
````js
//Subscribe to the event
abp.event.on('basketUpdated', function(basket) {
console.log('The new basket object: ');
console.log(basket);
});
//Trigger the event
abp.event.trigger('basketUpdated', {
items: [
{
"productId": "123",
"count": 2
},
{
"productId": "832",
"count": 1
}
]
});
````
### Multiple Arguments
If you want to pass multiple arguments, you can pass like `abp.event.on('basketUpdated', arg0, arg1, agr2)`. Then you can add the same argument list to the callback function on the subscriber side.
> **Tip:** Alternatively, you can send a single object that has a separate field for each argument. This makes easier to extend/change the event arguments in the future without breaking the subscribers.

15
docs/en/UI/AspNetCore/JavaScript-API/Features.md

@ -0,0 +1,15 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Features API
`abp.features` API allows you to check features or get the values of the features on the client side. You can read the current value of a feature in the client side only if it is allowed by the feature definition (on the server side).
> This document only explains the JavaScript API. See the [Features](../../../Features.md) document to understand the ABP Features system.
## Basic Usage
`abp.features.values` can be used to access to the all feature values.
````js
var excelExportFeatureValue = abp.features.values["ExportingToExcel"];
````
Then you can check the value of the feature to perform your logic.

32
docs/en/UI/AspNetCore/JavaScript-API/Index.md

@ -1,23 +1,19 @@
# JavaScript API
ABP provides some JavaScript APIs for ASP.NET Core MVC / Razor Pages applications. They can be used to perform some common application requirements in the client side.
ABP provides a set of JavaScript APIs for ASP.NET Core MVC / Razor Pages applications. They can be used to perform common application requirements easily in the client side and integrate to the server side.
## APIs
* abp.ajax
* abp.auth
* abp.currentUser
* abp.dom
* abp.event
* abp.features
* abp.localization
* abp.log
* abp.ModalManager
* abp.notify
* abp.security
* abp.setting
* abp.ui
* abp.utils
* abp.ResourceLoader
* abp.WidgetManager
* Other APIs
* [AJAX](Ajax.md)
* [Auth](Auth.md)
* [CurrentUser](CurrentUser.md)
* [DOM](DOM.md)
* [Events](Events.md)
* [Features](Features.md)
* [Localization](Localization.md)
* [Logging](Logging.md)
* [ResourceLoader](ResourceLoader.md)
* [Settings](Settings.md)
* [UI Block/Busy](Block-Busy.md)
* [UI Message](Message.md)
* [UI Notification](Notify.md)

143
docs/en/UI/AspNetCore/JavaScript-API/Localization.md

@ -0,0 +1,143 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Localization API
Localization API allows you to reuse the server side localization resources in the client side.
> This document only explains the JavaScript API. See the [localization document](../../../Localization.md) to understand the ABP localization system.
## Basic Usage
`abp.localization.getResource(...)` function is used to get a localization resource:
````js
var testResource = abp.localization.getResource('Test');
````
Then you can localize a string based on this resource:
````js
var str = testResource('HelloWorld');
````
`abp.localization.localize(...)` function is a shortcut where you can both specify the text name and the resource name:
````js
var str = abp.localization.localize('HelloWorld', 'Test');
````
`HelloWorld` is the text to localize, where `Test` is the localization resource name here.
### Fallback Logic
If given texts was not localized, localization method returns the given key as the localization result.
### Default Localization Resource
If you don't specify the localization resource name, it uses the **default localization resource** defined on the `AbpLocalizationOptions` (see the [localization document](../../../Localization.md)).
**Example: Using the default localization resource**
````js
var str = abp.localization.localize('HelloWorld'); //uses the default resource
````
### Format Arguments
If your localized string contains arguments, like `Hello {0}, welcome!`, you can pass arguments to the localization methods. Examples:
````js
var testSource = abp.localization.getResource('Test');
var str1 = testSource('HelloWelcomeMessage', 'John');
var str2 = abp.localization.localize('HelloWelcomeMessage', 'Test', 'John');
````
Assuming the `HelloWelcomeMessage` is localized as `Hello {0}, welcome!`, both of the samples above produce the output `Hello John, welcome!`.
## Other Properties & Methods
### abp.localization.values
`abp.localization.values` property stores all the localization resources, keys and their values.
### abp.localization.isLocalized
Returns a boolean indicating that if the given text was localized or not.
**Example**
````js
abp.localization.isLocalized('ProductName', 'MyResource');
````
Returns `true` if the `ProductName` text was localized for the `MyResource` resource. Otherwise, returns `false`. You can leave the resource name empty to use the default localization resource.
### abp.localization.defaultResourceName
`abp.localization.defaultResourceName` can be set to change the default localization resource. You normally don't set this since the ABP Framework automatically sets is based on the server side configuration.
### abp.localization.currentCulture
`abp.localization.currentCulture` returns an object to get information about the **currently selected language**.
An example value of this object is shown below:
````js
{
"displayName": "English",
"englishName": "English",
"threeLetterIsoLanguageName": "eng",
"twoLetterIsoLanguageName": "en",
"isRightToLeft": false,
"cultureName": "en",
"name": "en",
"nativeName": "English",
"dateTimeFormat": {
"calendarAlgorithmType": "SolarCalendar",
"dateTimeFormatLong": "dddd, MMMM d, yyyy",
"shortDatePattern": "M/d/yyyy",
"fullDateTimePattern": "dddd, MMMM d, yyyy h:mm:ss tt",
"dateSeparator": "/",
"shortTimePattern": "h:mm tt",
"longTimePattern": "h:mm:ss tt"
}
}
````
### abp.localization.languages
Used to get list of all **available languages** in the application. An example value of this object is shown below:
````js
[
{
"cultureName": "en",
"uiCultureName": "en",
"displayName": "English",
"flagIcon": null
},
{
"cultureName": "fr",
"uiCultureName": "fr",
"displayName": "Français",
"flagIcon": null
},
{
"cultureName": "pt-BR",
"uiCultureName": "pt-BR",
"displayName": "Português",
"flagIcon": null
},
{
"cultureName": "tr",
"uiCultureName": "tr",
"displayName": "Türkçe",
"flagIcon": null
},
{
"cultureName": "zh-Hans",
"uiCultureName": "zh-Hans",
"displayName": "简体中文",
"flagIcon": null
}
]
````

50
docs/en/UI/AspNetCore/JavaScript-API/Logging.md

@ -0,0 +1,50 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Logging API
`abp.log` API is used to write simple logs in the client side.
> The logs are written to console, using the `console.log`, by default.
> This document is for simple client side logging. See the [Logging](../../../Logging.md) document for server side logging system.
## Basic Usage
Use one of the `abp.log.xxx(...)` methods based on the severity of your log message.
````js
abp.log.debug("Some debug log here..."); //Logging a simple debug message
abp.log.info({ name: "john", age: 42 }); //Logging an object as an information log
abp.log.warn("A warning message"); //Logging a warning message
abp.log.error('An error happens...'); //Error message
abp.log.fatal('Network connection has gone away!'); //Fatal error
````
## Log Levels
There are 5 levels for a log message:
* DEBUG = 1
* INFO = 2
* WARN = 3
* ERROR = 4
* FATAL = 5
These are defined in the `abp.log.levels` object (like `abp.log.levels.WARN`).
### Changing the Current Log Level
You can control the log level as shown below:
````js
abp.log.level = abp.log.levels.WARN;
````
Default log level is `DEBUG`.
### Logging with Specifying the Level
Instead of calling `abp.log.info(...)` function, you can use the `abp.log.log` by specifying the log level as a parameter:
````js
abp.log.log("log message...", abp.log.levels.INFO);
````

128
docs/en/UI/AspNetCore/JavaScript-API/Message.md

@ -0,0 +1,128 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Message API
Message API is used to show nice looking messages to the user as a blocking dialog. Message API is an abstraction provided by the ABP Framework and implemented using the [SweetAlert2](https://sweetalert2.github.io/) library by default.
## Quick Example
Use `abp.message.success(...)` function to show a success message:
````js
abp.message.success('Your changes have been successfully saved!', 'Congratulations');
````
It will show a dialog on the UI:
![js-message-success](../../../images/js-message-success.png)
## Informative Messages
There are four types of informative message functions:
* `abp.message.info(...)`
* `abp.message.success(...)`
* `abp.message.warn(...)`
* `abp.message.error(...)`
All these methods get two parameters:
* `message`: The message (`string`) to be shown.
* `title`: An optional (`string`) title.
**Example: Show an error message**
````js
abp.message.error('Your credit card number is not valid!');
````
![js-message-error](../../../images/js-message-error.png)
## Confirmation Message
`abp.message.confirm(...)` function can be used to get a confirmation from the user.
**Example**
Use the following code to get a confirmation result from the user:
````js
abp.message.confirm('Are you sure to delete the "admin" role?')
.then(function(confirmed){
if(confirmed){
console.log('TODO: deleting the role...');
}
});
````
The resulting UI will be like shown below:
![js-message-confirm](../../../images/js-message-confirm.png)
If user has clicked the `Yes` button, the `confirmed` argument in the `then` callback function will be `true`.
> "*Are you sure?*" is the default title (localized based on the current language) and you can override it.
### The Return Value
The return value of the `abp.message.confirm(...)` function is a promise, so you can chain a `then` callback as shown above.
### Parameters
`abp.message.confirm(...)` function has the following parameters:
* `message`: A message (string) to show to the user.
* `titleOrCallback` (optional): A title or a callback function. If you supply a string, it is shown as the title. If you supply a callback function (that gets a `bool` parameter) then it's called with the result.
* `callback` (optional): If you've passes a title to the second parameter, you can pass your callback function as the 3rd parameter.
Passing a callback function is an alternative to the `then` callback shown above.
**Example: Providing all the parameters and getting result with the callback function**
````js
abp.message.confirm(
'Are you sure to delete the "admin" role?',
'Be careful!',
function(confirmed){
if(confirmed){
console.log('TODO: deleting the role...');
}
});
````
## SweetAlert Configuration
The Message API is implemented using the [SweetAlert2](https://sweetalert2.github.io/) library by default. If you want to change its configuration, you can set the options in the `abp.libs.sweetAlert.config` object. The default configuration object is shown below:
````js
{
'default': {
},
info: {
icon: 'info'
},
success: {
icon: 'success'
},
warn: {
icon: 'warning'
},
error: {
icon: 'error'
},
confirm: {
icon: 'warning',
title: 'Are you sure?',
buttons: ['Cancel', 'Yes']
}
}
````
> "Are you sure?", "Cancel" and "Yes" texts are automatically localized based on the current language.
So, if you want to set the `warn` icon, you can set it like:
````js
abp.libs.sweetAlert.config.warn.icon = 'error';
````
See the [SweetAlert document](https://sweetalert2.github.io/) for all the configuration options.

45
docs/en/UI/AspNetCore/JavaScript-API/Notify.md

@ -0,0 +1,45 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Notify API
Notify API is used to show toast style, auto disappearing UI notifications to the end user. It is implemented by the [Toastr](https://github.com/CodeSeven/toastr) library by default.
## Quick Example
Use `abp.notify.success(...)` function to show a success message:
````js
abp.notify.success(
'The product "Acme Atom Re-Arranger" has been successfully deleted.',
'Deleted the Product'
);
````
A notification message is shown at the bottom right of the page:
![js-message-success](../../../images/js-notify-success.png)
## Notification Types
There are four types of pre-defined notifications;
* `abp.notify.success(...)`
* `abp.notify.info(...)`
* `abp.notify.warn(...)`
* `abp.notify.error(...)`
All of the methods above gets the following parameters;
* `message`: A message (`string`) to show to the user.
* `title`: An optional title (`string`).
* `options`: Additional options to be passed to the underlying library, to the Toastr by default.
## Toastr Configuration
The notification API is implemented by the [Toastr](https://github.com/CodeSeven/toastr) library by default. You can see its own configuration options.
**Example: Show toast messages on the top right of the page**
````js
toastr.options.positionClass = 'toast-top-right';
````
> ABP sets this option to `toast-bottom-right` by default. You can override it just as shown above.

40
docs/en/UI/AspNetCore/JavaScript-API/ResourceLoader.md

@ -0,0 +1,40 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Resource Loader API
`abp.ResourceLoader` is a service that can load a JavaScript or CSS file on demand. It guarantees to load the file only once even if you request multiple times.
## Loading Script Files
`abp.ResourceLoader.loadScript(...)` function **loads** a JavaScript file from the server and **executes** it.
**Example: Load a JavaScript file**
````js
abp.ResourceLoader.loadScript('/Pages/my-script.js');
````
### Parameters
`loadScript` function can get three parameters;
* `url` (required, `string`): The URL of the script file to be loaded.
* `loadCallback` (optional, `function`): A callback function that is called once the script is loaded & executed. In this callback you can safely use the code in the script file. This callback is called even if the file was loaded before.
* `failCallback` (optional, `function`): A callback function that is called if loading the script fails.
**Example: Provide the `loadCallback` argument**
````js
abp.ResourceLoader.loadScript('/Pages/my-script.js', function() {
console.log('successfully loaded :)');
});
````
## Loading Style Files
`abp.ResourceLoader.loadStyle(...)` function adds a `link` element to the `head` of the document for the given URL, so the CSS file is automatically loaded by the browser.
**Example: Load a CSS file**
````js
abp.ResourceLoader.loadStyle('/Pages/my-styles.css');
````

33
docs/en/UI/AspNetCore/JavaScript-API/Settings.md

@ -0,0 +1,33 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Setting API
Localization API allows you to get the values of the settings on the client side. You can read the current value of a setting in the client side only if it is allowed by the setting definition (on the server side).
> This document only explains the JavaScript API. See the [settings document](../../../Settings.md) to understand the ABP setting system.
## Basic Usage
````js
//Gets a value as string.
var language = abp.setting.get('Abp.Localization.DefaultLanguage');
//Gets an integer value.
var requiredLength = abp.setting.getInt('Abp.Identity.Password.RequiredLength');
//Gets a boolean value.
var requireDigit = abp.setting.getBoolean('Abp.Identity.Password.RequireDigit');
````
## All Values
`abp.setting.values` can be used to obtain all the setting values as an object where the object properties are setting names and property values are the setting values.
An example value of this object is shown below:
````js
{
Abp.Localization.DefaultLanguage: "en",
Abp.Timing.TimeZone: "UTC",
...
}
````

106
docs/en/UI/AspNetCore/Layout-Hooks.md

@ -1,3 +1,105 @@
# Layout Hooks
# ASP.NET Core MVC / Razor Pages Layout Hooks
TODO
ABP Framework theming system places the page layout into the [theme](Theming.md) NuGet packages. That means the final application doesn't include a `Layout.cshtml`, so you can't directly change the layout code to customize it.
You copy the theme code into your solution. In this case you are completely free to customize it. However, then you won't be able to get automatic updates of the theme (by upgrading the theme NuGet package).
ABP Framework provides different ways of [customizing the UI](Customization-User-Interface.md).
The **Layout Hook System** allows you to **add code** at some specific parts of the layout. All layouts of all themes should implement these hooks. Finally, you can add a **view component** into a hook point.
## Example: Add Google Analytics Script
Assume that you need to add the Google Analytics script to the layout (that will be available for all the pages). First, **create a view component** in your project:
![bookstore-google-analytics-view-component](../../images/bookstore-google-analytics-view-component.png)
**NotificationViewComponent.cs**
````csharp
public class GoogleAnalyticsViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View("/Pages/Shared/Components/GoogleAnalytics/Default.cshtml");
}
}
````
**Default.cshtml**
````html
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-xxxxxx-1', 'auto');
ga('send', 'pageview');
</script>
````
Change `UA-xxxxxx-1` with your own code.
You can then add this component to any of the hook points in the `ConfigureServices` of your module:
````csharp
Configure<AbpLayoutHookOptions>(options =>
{
options.Add(
LayoutHooks.Head.Last, //The hook name
typeof(GoogleAnalyticsViewComponent) //The component to add
);
});
````
Now, the GA code will be inserted in the `head` of the page as the last item.
### Specifying the Layout
The configuration above adds the `GoogleAnalyticsViewComponent` to all layouts. You may want to only add to a specific layout:
````csharp
Configure<AbpLayoutHookOptions>(options =>
{
options.Add(
LayoutHooks.Head.Last,
typeof(GoogleAnalyticsViewComponent),
layout: StandardLayouts.Application //Set the layout to add
);
});
````
See the *Layouts* section below to learn more about the layout system.
## Layout Hook Points
There are some pre-defined layout hook points. The `LayoutHooks.Head.Last` used above was one of them. The standard hook points are;
* `LayoutHooks.Head.First`: Used to add a component as the first item in the HTML head tag.
* `LayoutHooks.Head.Last`: Used to add a component as the last item in the HTML head tag.
* `LayoutHooks.Body.First`: Used to add a component as the first item in the HTML body tag.
* `LayoutHooks.Body.Last`: Used to add a component as the last item in the HTML body tag.
* `LayoutHooks.PageContent.First`: Used to add a component just before the page content (the `@RenderBody()` in the layout).
* `LayoutHooks.PageContent.Last`: Used to add a component just after the page content (the `@RenderBody()` in the layout).
> You (or the modules you are using) can add **multiple items to the same hook point**. All of them will be added to the layout by the order they were added.
## Layouts
Layout system allows themes to define standard, named layouts and allows any page to select a proper layout for its purpose. There are three pre-defined layouts:
* "**Application**": The main (and the default) layout for an application. It typically contains header, menu (sidebar), footer, toolbar... etc.
* "**Account**": This layout is used by login, register and other similar pages. It is used for the pages under the `/Pages/Account` folder by default.
* "**Empty**": Empty and minimal layout.
These names are defined in the `StandardLayouts` class as constants. You can definitely create your own layouts, but these are the standard layout names and implemented by all the themes out of the box.
### Layout Location
You can find the layout files [here](https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts) for the basic theme. You can take them as references to build your own layouts or you can override them if necessary.
## See Also
* [Customizing the User Interface](Customization-User-Interface.md)

483
docs/en/UI/AspNetCore/Modals.md

@ -0,0 +1,483 @@
# ASP.NET Core MVC / Razor Pages UI: Modals
While you can continue to use the standard [Bootstrap way](https://getbootstrap.com/docs/4.5/components/modal/) to create, open and manage modals in your applications, ABP Framework provides a **flexible** way to manage modals by **automating common tasks** for you.
**Example: A modal dialog to create a new role entity**
![modal-manager-example-modal](../../images/modal-manager-example-modal.png)
ABP Framework provides the following benefits for such a modal with a form inside it;
* **Lazy loads** the modal HTML into the page and **removes** it from the DOM once its closed. This makes easy to consume a reusable modal dialog. Also, every time you open the modal, it will be a fresh new modal, so you don't have to deal with resetting the modal content.
* **Auto-focuses** the first input of the form once the modal has been opened.
* Automatically determines the **form** inside a modal and posts the form via **AJAX** instead of normal page post.
* Automatically checks if the form inside the modal **has changed, but not saved**. It warns the user in this case.
* Automatically **disables the modal buttons** (save & cancel) until the AJAX operation completes.
* Makes it easy to register a **JavaScript object that is initialized** once the modal has loaded.
So, it makes you write less code when you deal with the modals, especially the modals with a form inside.
## Basic Usage
### Creating a Modal as a Razor Page
To demonstrate the usage, we are creating a simple Razor Page, named `ProductInfoModal.cshtml`, under the `/Pages/Products` folder:
![modal-page-on-rider](../../images/modal-page-on-rider.png)
**ProductInfoModal.cshtml Content:**
````html
@page
@model MyProject.Web.Pages.Products.ProductInfoModalModel
@{
Layout = null;
}
<abp-modal>
<abp-modal-header title="Product Information"></abp-modal-header>
<abp-modal-body>
<h3>@Model.ProductName</h3>
<div>
<img src="@Model.ProductImageUrl" />
</div>
<p>
@Model.ProductDescription
</p>
<p>
<small><i>Reference: https://acme.com/catalog/</i></small>
</p>
</abp-modal-body>
<abp-modal-footer buttons="Close"></abp-modal-footer>
</abp-modal>
````
* This page sets the `Layout` to `null` since we will show this as a modal. So, no need to wrap with a layout.
* It uses [abp-modal tag helper](Tag-Helpers/Modals.md) to simplify creating the modal HTML code. You can use the standard Bootstrap modal code if you prefer it.
**ProductInfoModalModel.cshtml.cs Content:**
```csharp
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace MyProject.Web.Pages.Products
{
public class ProductInfoModalModel : AbpPageModel
{
public string ProductName { get; set; }
public string ProductDescription { get; set; }
public string ProductImageUrl { get; set; }
public void OnGet()
{
ProductName = "Acme Indestructo Steel Ball";
ProductDescription = "The ACME Indestructo Steel Ball is completely indestructible, there is nothing that can destroy it!";
ProductImageUrl = "https://acme.com/catalog/acmeindestructo.jpg";
}
}
}
```
You can surely get the product info from a database or API. We are setting the properties hard-coded for the sake of simplicity,
### Defining the Modal Manager
Once you have a modal, you can open it in any page using some simple **JavaScript** code.
First, create an `abp.ModalManager` object by setting the `viewUrl`, in the JavaScript file of the page that will use the modal:
````js
var productInfoModal = new abp.ModalManager({
viewUrl: '/Products/ProductInfoModal'
});
````
> If you only need to specify the `viewUrl`, you can directly pass it to the `ModalManager` constructor, as a shortcut. Example: `new abp.ModalManager('/Products/ProductInfoModal');`
### Opening the Modal
Then open the modal whenever you need:
````js
productInfoModal.open();
````
You typically want to open the modal when something happens; For example, when the user clicks a button:
````js
$('#OpenProductInfoModal').click(function(){
productInfoModal.open();
});
````
The resulting modal will be like that:
![modal-example-product-info](../../images/modal-example-product-info.png)
#### Opening the Modal with Arguments
When you call the `open()` method, `ModalManager` loads the modal HTML by requesting it from the `viewUrl`. You can pass some **query string parameters** to this URL when you open the modal.
**Example: Pass the product id while opening the modal**
````js
productInfoModal.open({
productId: 42
});
````
You can add a `productId` parameter to the get method:
````csharp
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace MyProject.Web.Pages.Products
{
public class ProductInfoModalModel : AbpPageModel
{
//...
public async Task OnGetAsync(int productId) //Add productId parameter
{
//TODO: Get the product with database with the given productId
//...
}
}
}
````
In this way, you can use the `productId` to query the product from a data source.
## Modals with Forms
`abp.ModalManager` handles various common tasks (described in the introduction) when you want to use a form inside the modal.
### Example Modal with a Form
This section shows an example form to create a new product.
#### Creating the Razor Page
For this example, creating a new Razor Page, named `ProductCreateModal.cshtml`, under the `/Pages/Products` folder:
![product-create-modal-page-on-rider](../../images/product-create-modal-page-on-rider.png)
**ProductCreateModal.cshtml Content:**
````html
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model MyProject.Web.Pages.Products.ProductCreateModalModel
@{
Layout = null;
}
<form method="post" action="@Url.Page("/Products/ProductCreateModal")">
<abp-modal>
<abp-modal-header title="Create New Product"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Product.Name"/>
<abp-input asp-for="Product.Description"/>
<abp-input asp-for="Product.ReleaseDate"/>
</abp-modal-body>
<abp-modal-footer buttons="@AbpModalButtons.Save | @AbpModalButtons.Cancel"></abp-modal-footer>
</abp-modal>
</form>
````
* The `abp-modal` has been wrapped by the `form`. This is needed to place the `Save` and the `Cancel` buttons into the form. In this way, the `Save` button acts as the `submit` button for the `form`.
* Used the [abp-input tag helpers](Tag-Helpers/Form-Elements.md) to simplify to create the form elements. Otherwise, you need to write more HTML.
**ProductCreateModal.cshtml.cs Content:**
```csharp
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace MyProject.Web.Pages.Products
{
public class ProductCreateModalModel : AbpPageModel
{
[BindProperty]
public PoductCreationDto Product { get; set; }
public async Task OnGetAsync()
{
//TODO: Get logic, if available
}
public async Task<IActionResult> OnPostAsync()
{
//TODO: Save the Product...
return NoContent();
}
}
}
```
* This is a simple `PageModal` class. The `[BindProperty]` make the form binding to the model when you post (submit) the form; The standard ASP.NET Core system.
* `OnPostAsync` returns `NoContent` (this method is defined by the base `AbpPageModel` class). Because we don't need to a return value in the client side, after the form post operation.
**PoductCreationDto:**
`ProductCreateModalModel` uses a `PoductCreationDto` class defined as shown below:
````csharp
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
namespace MyProject.Web.Pages.Products
{
public class PoductCreationDto
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[TextArea(Rows = 4)]
[StringLength(2000)]
public string Description { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
}
}
````
* `abp-input` Tag Helper can understand the data annotation attributes and uses them to shape and validate the form elements. See the [abp-input tag helpers](Tag-Helpers/Form-Elements.md) document to learn more.
#### Defining the Modal Manager
Again, create an `abp.ModalManager` object by setting the `viewUrl`, in the JavaScript file of the page that will use the modal:
````js
var productCreateModal = new abp.ModalManager({
viewUrl: '/Products/ProductCreateModal'
});
````
#### Opening the Modal
Then open the modal whenever you need:
````js
productCreateModal.open();
````
You typically want to open the modal when something happens; For example, when the user clicks a button:
````js
$('#OpenProductCreateModal').click(function(){
productCreateModal.open();
});
````
So, the complete code will be something like that (assuming you have a `button` with `id` is `OpenProductCreateModal` on the view side):
```js
$(function () {
var productCreateModal = new abp.ModalManager({
viewUrl: '/Products/ProductCreateModal'
});
$('#OpenProductCreateModal').click(function () {
productCreateModal.open();
});
});
```
The resulting modal will be like that:
![modal-example-product-create](../../images/modal-example-product-create.png)
#### Saving the Modal
When you click to the `Save` button, the form is posted to the server. If the server returns a **success response**, then the `onResult` event is triggered with some arguments including the server response and the modal is automatically closed.
An example callback that logs the arguments passed to the `onResult` method:
````js
productCreateModal.onResult(function(){
console.log(arguments);
});
````
If the server returns a failed response, it shows the error message returned from the server and keeps the modal open.
> See the *Modal Manager Reference* section below for other modal events.
#### Canceling the Modal
If you click to the Cancel button with some changes made but not saved, you get such a warning message:
![modal-manager-cancel-warning](../../images/modal-manager-cancel-warning.png)
If you don't want such a check & message, you can add `data-check-form-on-close="false"` attribute to your `form` element. Example:
````html
<form method="post"
action="@Url.Page("/Products/ProductCreateModal")"
data-check-form-on-close="false">
````
### Form Validation
`ModalManager` automatically triggers the form validation when you click to the `Save` button or hit the `Enter` key on the form:
![modal-manager-validation](../../images/modal-manager-validation.png)
See the [Forms & Validation document](Forms-Validation.md) to learn more about the validation.
## Modals with Script Files
You may need to perform some logic for your modal. To do that, create a JavaScript file like below:
````js
abp.modals.ProductInfo = function () {
function initModal(modalManager, args) {
var $modal = modalManager.getModal();
var $form = modalManager.getForm();
$modal.find('h3').css('color', 'red');
console.log('initialized the modal...');
};
return {
initModal: initModal
};
};
````
* This code simply adds a `ProductInfo` class into the `abp.modals` namespace. The `ProductInfo` class exposes a single public function: `initModal`.
* `initModal` method is called by the `ModalManager` once the modal HTML is inserted to DOM and ready for the initialization logic.
* `modalManager` parameter is the `ModalManager` object related to this modal instance. So, you can use any function on it in your code. See the *ModalManager Reference* section.
Then include this file to the page that you use the modal:
````html
<abp-script src="/Pages/Products/ProductInfoModal.js"/>
<abp-script src="/Pages/Products/Index.js"/>
````
* We've use the `abp-script` Tag Helper here. See the [Bundling & Minification](Bundling-Minification.md) document if you want to understand it. You can use the standard `script` tag. It doesn't matter for this case.
Finally, set the `modalClass` option while creating the `ModalManager` instance:
````js
var productInfoModal = new abp.ModalManager({
viewUrl: '/Products/ProductInfoModal',
modalClass: 'ProductInfo' //Matches to the abp.modals.ProductInfo
});
````
### Lazy Loading the Script File
Instead of adding the `ProductInfoModal.js` to the page you use the modal, you can configure it to lazy load the script file when the first time the modal is opened.
Example:
````js
var productInfoModal = new abp.ModalManager({
viewUrl: '/Products/ProductInfoModal',
scriptUrl: '/Pages/Products/ProductInfoModal.js', //Lazy Load URL
modalClass: 'ProductInfo'
});
````
* `scriptUrl` is used to set the URL to load the script file of the modal.
* In this case, you no longer need to include the `ProductInfoModal.js` to the page. It will be loaded on demand.
#### Tip: Bundling & Minification
While lazy loading seems cool at the beginning, it requires an additional call to the server when you first open the modal.
Instead, you can use the [Bundling & Minification](Bundling-Minification.md) system to create a bundle (that is a single and minified file on production) for all the used script files for a page:
````html
<abp-script-bundle>
<abp-script src="/Pages/Products/ProductInfoModal.js"/>
<abp-script src="/Pages/Products/Index.js"/>
</abp-script-bundle>
````
This is efficient if the script file is not large and frequently opened while users use the page.
Alternatively, you can define the `abp.modals.ProductInfo` class in the page's main JavaScript file if the modal is only and always used in the same page. In this case, you don't need to another external script file at all.
## ModalManager Reference
### Options
Options can be passed when you create a new `ModalManager` object:
````js
var productInfoModal = new abp.ModalManager({
viewUrl: '/Products/ProductInfoModal',
//...other options
});
````
Here, the list of all available options;
* `viewUrl` (required, `string`): The URL to lazy load the HTML of the modal.
* `scriptUrl` (optional, `string`): A URL to lazy load a JavaScript file. It is loaded only once, when the modal first opened.
* `modalClass` (optional, `string`): A JavaScript class defined in the `abp.modals` namespace that can be used to execute code related to the modal.
### Functions
When you create a new `ModalManager` object, you can use its functions to perform operations on the modal. Example:
````js
var myModal = new abp.ModalManager({
//...options
});
//Open the modal
myModal.open();
//Close the modal
myModal.close();
````
Here, the list of all available functions of the `ModalManager` object;
* `open([args])`: Opens the modal dialog. It can get an `args` object that is converted to query string while getting the `viewUrl` from the server. For example, if `args` is `{ productId: 42 }`, then the `ModalManager` passes `?productId=42` to the end of the `viewUrl` while loading the view from the server.
* `reopen()`: Opens the modal with the latest provided `args` for the `open()` method. So, it is a shortcut if you want to re-open the modal with the same `args`.
* `close()`: Closes the modal. The modal HTML is automatically removed from DOM once it has been closed.
* `getModalId()`: Gets the `id` attribute of the container that contains the view returned from the server. This is a unique id per modal and it doesn't change after you create the `ModalManager`.
* `getModal()`: Returns the modal wrapper DOM element (the HTML element with the `modal` CSS class) as a JQuery selection, so you can perform any JQuery method on it.
* `getForm()`: Returns the `form` HTML element as a JQuery selection, so you can perform any JQuery method on it. It returns `null` if the modal has no form inside it.
* `getArgs()` Gets the latest arguments object provided while opening the modal.
* `getOptions()`: Gets the options object passed to the `ModalManager` constructor.
* `setResult(...)`: Triggers the `onResult` event with the provided arguments. You can pass zero or more arguments those are directly passed to the `onResult` event. This function is generally called by the modal script to notify the page that uses the modal.
### Events
When you create a new `ModalManager` object, you can use its functions register to events of the modal. Examples:
````js
var myModal = new abp.ModalManager({
//...options
});
myModal.onOpen(function () {
console.log('opened the modal...');
});
myModal.onClose(function () {
console.log('closed the modal...');
});
````
Here, the list of all available functions to register to events of the `ModalManager` object;
* `onOpen(callback)`: Registers a callback function to get notified once the modal is opened. It is triggered when the modal is completely visible on the UI.
* `onClose(callback)`: Registers a callback function to get notified once the modal is closed. It is triggered when the modal is completely invisible on the UI.
* `onResult(callback)`: Registers a callback function that is triggered when the ``setResult(...)` method is called. All the parameters sent to the `setResult` method is passed to the callback.

10
docs/en/UI/AspNetCore/Tag-Helpers/Badges.md

@ -2,11 +2,11 @@
## Introduction
`abp-badge` and `abp-badge-pill` are abp tags for badges.
`abp-badge` and `abp-badge-pill` are ABP Tag Helper attributes for `a` and `span` html tags.
Basic usage:
````csharp
````html
<span abp-badge="Primary">Primary</span>
<a abp-badge="Info" href="#">Info</a>
<a abp-badge-pill="Danger" href="#">Danger</a>
@ -22,8 +22,7 @@ See the [badges demo page](https://bootstrap-taghelpers.abp.io/Components/Badges
* Indicates the type of the badge. Should be one of the following values:
* `_` (default value)
* `Default` (default value)
* `Default`
* `Primary`
* `Secondary`
* `Success`
@ -35,6 +34,7 @@ See the [badges demo page](https://bootstrap-taghelpers.abp.io/Components/Badges
Example:
````csharp
````html
<span abp-badge-pill="Danger">Danger</span>
````

18
docs/en/UI/AspNetCore/Tag-Helpers/Blockquote.md

@ -0,0 +1,18 @@
# Blockquote
`abp-blockquote` is the main container for blockquote items.
Basic usages:
````html
<abp-blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p>
</abp-blockquote>
<abp-blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p>
<footer>Someone famous in Source Title</footer>
</abp-blockquote>
````
It adds `blockquote` class to main container, also adds `blockquote-footer` class to inner `footer` element and `mb-0` class to inner `p` element.

6
docs/en/UI/AspNetCore/Tag-Helpers/Borders.md

@ -2,11 +2,11 @@
## Introduction
`abp-border` is a main element for border styling.
`abp-border` is ABP Tag Helper attribute for border styling.
Basic usage:
````csharp
````html
<span abp-border="Default"></span>
<span abp-border="Top"></span>
<span abp-border="Right"></span>
@ -124,3 +124,5 @@ A value indicates type, position and the color of the border. Should be one of t
* `Bottom_Light_0`
* `Bottom_Dark_0`
* `Bottom_White_0`
(Values with `_0` at the end is for [Subtractive](https://getbootstrap.com/docs/4.0/utilities/borders/#subtractive) usages)

2
docs/en/UI/AspNetCore/Tag-Helpers/Breadcrumbs.md

@ -6,7 +6,7 @@
Basic usage:
````csharp
````html
<abp-breadcrumb>
<abp-breadcrumb-item href="#" title="Home" />
<abp-breadcrumb-item href="#" title="Library"/>

2
docs/en/UI/AspNetCore/Tag-Helpers/Button-groups.md

@ -6,7 +6,7 @@
Basic usage:
````csharp
````html
<abp-button-group>
<abp-button button-type="Secondary">Left</abp-button>
<abp-button button-type="Secondary">Middle</abp-button>

2
docs/en/UI/AspNetCore/Tag-Helpers/Carousel.md

@ -6,7 +6,7 @@
Basic usage:
````csharp
````html
<abp-carousel>
<abp-carousel-item src=""></abp-carousel-item>
<abp-carousel-item src=""></abp-carousel-item>

2
docs/en/UI/AspNetCore/Tag-Helpers/Collapse.md

@ -6,7 +6,7 @@
Basic usage:
````xml
````html
<abp-button button-type="Primary" abp-collapse-id="collapseExample" text="Button with data-target" />
<a abp-button="Primary" abp-collapse-id="collapseExample"> Link with href </a>

14
docs/en/UI/AspNetCore/Tag-Helpers/Figure.md

@ -0,0 +1,14 @@
# Figures
`abp-figure` is the main container for bootstrap figure items.
Basic usage:
````html
<abp-figure>
<abp-image src="..." class="img-fluid rounded" alt="A generic square placeholder image with rounded corners in a figure.">
<abp-figcaption class="text-right">A caption for the above image.</abp-figcaption>
</abp-figure>
````
It adds `figure` class to main container, also adds `figure-img` class to inner `abp-image` element and `figure-caption` class to inner `abp-figcaption` element.

9
docs/en/UI/AspNetCore/Tag-Helpers/Index.md

@ -13,19 +13,26 @@ ABP Framework also adds some **useful features** to the standard bootstrap compo
Here, the list of components those are wrapped by the ABP Framework:
* [Alerts](Alerts.md)
* [Badges](Badges.md))
* [Blockquote](Blockquote.md)
* [Borders](Borders.md)
* [Breadcrumb](Breadcrumb.md)
* [Buttons](Buttons.md)
* [Cards](Cards.md)
* [Carousel](Carousel.md)
* [Collapse](Collapse.md)
* [Dropdowns](Dropdowns.md)
* [Figures](Figure.md)
* [Grids](Grids.md)
* [List Groups](List-Groups.md)
* [Modals](Modals.md)
* [Navigation](Navs.md)
* [Paginator](Paginator.md)
* [Popovers](Popovers.md)
* [Progress Bars](Progress-Bars.md)
* [Tables](Tables.md)
* [Tabs](Tabs.md)
* [Tooltips](Tooltips.md)
* ...
> Until all the tag helpers are documented, you can visit https://bootstrap-taghelpers.abp.io/ to see them with live samples.

4
docs/en/UI/AspNetCore/Tag-Helpers/Modals.md

@ -1,5 +1,7 @@
# Modals
> This document explains the details of the `abp-modal` Tag Helper, which simplifies to build the HTML markup for a modal dialog. Read [that documentation](../Modals.md) to learn how to work with modals.
## Introduction
`abp-modal` is a main element to create a modal.
@ -18,8 +20,6 @@ Basic usage:
</abp-modal>
````
## Demo
See the [modals demo page](https://bootstrap-taghelpers.abp.io/Components/Modals) to see it in action.

4
docs/en/UI/AspNetCore/Tag-Helpers/Navs.md

@ -6,7 +6,7 @@
Basic usage:
````csharp
````html
<abp-nav nav-style="Pill" align="Center">
<abp-nav-item>
<a abp-nav-link active="true" href="#">Active</a>
@ -78,7 +78,7 @@ See the [navs demo page](https://bootstrap-taghelpers.abp.io/Components/Navs) to
Example:
````csharp
````html
<abp-nav-bar size="Lg" navbar-style="Dark_Warning">
<a abp-navbar-brand href="#">Navbar</a>
<abp-navbar-toggle>

2
docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md

@ -12,7 +12,7 @@ Basic usage:
Model:
````xml
````csharp
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination;

2
docs/en/UI/AspNetCore/Tag-Helpers/Tables.md

@ -6,7 +6,7 @@
Basic usage:
````csharp
````html
<abp-table hoverable-rows="true" responsive-sm="true">
<thead>
<tr>

3
docs/en/UI/Blazor/Localization.md

@ -0,0 +1,3 @@
# Blazor UI: Localization
Blazor applications can reuse the same `IStringLocalizer<T>` service that is explained in the [localization document](../../Localization.md). All the localization resources and texts available in the server side are usable in the Blazor application.

3
docs/en/UI/Blazor/Settings.md

@ -0,0 +1,3 @@
# Blazor UI: Settings
Blazor applications can reuse the same `ISettingProvider` service that is explained in the [settings document](../../Settings.md).

83
docs/en/docs-nav.json

@ -99,11 +99,15 @@
"path": "CLI.md"
},
{
"text": "Authentication",
"text": "Authentication & Security",
"items": [
{
"text": "Social/External Logins",
"path": "Authentication/Social-External-Logins.md"
},
{
"text": "CSRF/XSRF & Anti Forgery",
"path": "CSRF-Anti-Forgery.md"
}
]
},
@ -212,6 +216,10 @@
{
"text": "Kafka Integration",
"path": "Distributed-Event-Bus-Kafka-Integration.md"
},
{
"text": "Rebus Integration",
"path": "Distributed-Event-Bus-Rebus-Integration.md"
}
]
}
@ -394,13 +402,17 @@
{
"text": "ASP.NET Core MVC / Razor Pages",
"items": [
{
"text": "Navigation / Menus",
"path": "UI/AspNetCore/Navigation-Menu.md"
},
{
"text": "Forms & Validation",
"path": "UI/AspNetCore/Forms-Validation.md"
},
{
"text": "Navigation / Menus",
"path": "UI/AspNetCore/Navigation-Menu.md"
"text": "Modals",
"path": "UI/AspNetCore/Modals.md"
},
{
"text": "Page Alerts",
@ -432,10 +444,75 @@
"text": "Widgets",
"path": "UI/AspNetCore/Widgets.md"
},
{
"text": "Layout Hooks",
"path": "UI/AspNetCore/Layout-Hooks.md"
},
{
"text": "Theming",
"path": "UI/AspNetCore/Theming.md"
},
{
"text": "JavaScript API",
"items": [
{
"text": "Overall",
"path": "UI/AspNetCore/JavaScript-API/Index.md"
},
{
"text": "Localization",
"path": "UI/AspNetCore/JavaScript-API/Localization.md"
},
{
"text": "Auth",
"path": "UI/AspNetCore/JavaScript-API/Auth.md"
},
{
"text": "Current User",
"path": "UI/AspNetCore/JavaScript-API/CurrentUser.md"
},
{
"text": "Settings",
"path": "UI/AspNetCore/JavaScript-API/Settings.md"
},
{
"text": "Features",
"path": "UI/AspNetCore/JavaScript-API/Features.md"
},
{
"text": "AJAX",
"path": "UI/AspNetCore/JavaScript-API/Ajax.md"
},
{
"text": "Message",
"path": "UI/AspNetCore/JavaScript-API/Message.md"
},
{
"text": "Notify",
"path": "UI/AspNetCore/JavaScript-API/Notify.md"
},
{
"text": "Block/Busy",
"path": "UI/AspNetCore/JavaScript-API/Block-Busy.md"
},
{
"text": "Events",
"path": "UI/AspNetCore/JavaScript-API/Events.md"
},
{
"text": "DOM",
"path": "UI/AspNetCore/JavaScript-API/DOM.md"
},
{
"text": "Logging",
"path": "UI/AspNetCore/JavaScript-API/Logging.md"
},
{
"text": "Resource Loader",
"path": "UI/AspNetCore/JavaScript-API/ResourceLoader.md"
}
]
},
{
"text": "Customize/Extend the UI",
"path": "UI/AspNetCore/Customization-User-Interface.md"

BIN
docs/en/images/ajax-error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/images/js-message-confirm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/en/images/js-message-error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/images/js-message-success.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/images/js-notify-success.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/en/images/modal-example-product-create.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
docs/en/images/modal-example-product-info.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

BIN
docs/en/images/modal-manager-cancel-warning.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
docs/en/images/modal-manager-example-modal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/en/images/modal-manager-validation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/images/modal-page-on-rider.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/images/product-create-modal-page-on-rider.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

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

Loading…
Cancel
Save