Browse Source

Merge remote-tracking branch 'abpframework/dev' into dev

pull/3470/head
liangshiwei 6 years ago
parent
commit
2a900fc8a8
  1. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json
  2. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  4. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  5. 2
      common.props
  6. 52
      docs/en/Application-Services.md
  7. 4
      docs/en/Authorization.md
  8. 6
      docs/en/Background-Jobs.md
  9. 139
      docs/en/Background-Workers.md
  10. 142
      docs/en/Blog-Posts/2020-03-19 v2_3_Release/Post.md
  11. BIN
      docs/en/Blog-Posts/2020-03-19 v2_3_Release/abp-io-release-2-3-0-blog.png
  12. BIN
      docs/en/Blog-Posts/2020-03-19 v2_3_Release/react-native-ui.png
  13. 39
      docs/en/CLI.md
  14. 9
      docs/en/Customizing-Application-Modules-Guide.md
  15. 2
      docs/en/Customizing-Application-Modules-Overriding-Services.md
  16. 5
      docs/en/Customizing-Application-Modules-Overriding-User-Interface.md
  17. 16
      docs/en/Entity-Framework-Core-Other-DBMS.md
  18. 28
      docs/en/Exception-Handling.md
  19. 3
      docs/en/IdentityServer-Integration.md
  20. 2
      docs/en/Modules/Blogging.md
  21. 160
      docs/en/Modules/Docs.md
  22. 12
      docs/en/Multi-Tenancy.md
  23. 1
      docs/en/Tutorials/Part-2.md
  24. 0
      docs/en/Tutorials/Part-3.md
  25. 290
      docs/en/UI/Angular/Config-State.md
  26. 8
      docs/en/UI/Angular/Custom-Setting-Page.md
  27. 209
      docs/en/UI/Angular/Http-Requests.md
  28. 6
      docs/en/UI/Angular/Localization.md
  29. 114
      docs/en/UI/Angular/Track-By-Service.md
  30. 12
      docs/en/UI/AspNetCore/Bundling-Minification.md
  31. 202
      docs/en/UI/AspNetCore/Customization-User-Interface.md
  32. 3
      docs/en/UI/AspNetCore/Layout-Hooks.md
  33. 3
      docs/en/UI/AspNetCore/Libraries/DatatablesNet.md
  34. 82
      docs/en/UI/AspNetCore/Tag-Helpers/Alerts.md
  35. 188
      docs/en/UI/AspNetCore/Tag-Helpers/Cards.md
  36. 92
      docs/en/UI/AspNetCore/Tag-Helpers/Collapse.md
  37. 97
      docs/en/UI/AspNetCore/Tag-Helpers/Dropdowns.md
  38. 286
      docs/en/UI/AspNetCore/Tag-Helpers/Grids.md
  39. 12
      docs/en/UI/AspNetCore/Tag-Helpers/Index.md
  40. 78
      docs/en/UI/AspNetCore/Tag-Helpers/List-Groups.md
  41. 81
      docs/en/UI/AspNetCore/Tag-Helpers/Modals.md
  42. 57
      docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md
  43. 70
      docs/en/UI/AspNetCore/Tag-Helpers/Progress-Bars.md
  44. 93
      docs/en/UI/AspNetCore/Tag-Helpers/Tabs.md
  45. 35
      docs/en/UI/AspNetCore/Tag-Helpers/Tooltips.md
  46. 3
      docs/en/UI/AspNetCore/Toolbars.md
  47. 14
      docs/en/UI/AspNetCore/Widgets.md
  48. 16
      docs/en/docs-nav.json
  49. BIN
      docs/en/images/bookstore-google-analytics-view-component.png
  50. BIN
      docs/en/images/bookstore-notification-icon-on-toolbar.png
  51. BIN
      docs/en/images/bookstore-notification-view-component.png
  52. BIN
      docs/en/images/bookstore-toolbar-highlighted.png
  53. BIN
      docs/en/images/docs-module_download-new-abp-project.png
  54. BIN
      docs/en/images/docs-module_solution-explorer.png
  55. 55
      docs/zh-Hans/Application-Services.md
  56. 3
      docs/zh-Hans/AspNet-Boilerplate-Migration-Guide.md
  57. 4
      docs/zh-Hans/Authorization.md
  58. 5
      docs/zh-Hans/Background-Jobs.md
  59. 137
      docs/zh-Hans/Background-Workers.md
  60. 142
      docs/zh-Hans/Blog-Posts/2020-03-19 v2_3_Release/Post.md
  61. BIN
      docs/zh-Hans/Blog-Posts/2020-03-19 v2_3_Release/abp-io-release-2-3-0-blog.png
  62. BIN
      docs/zh-Hans/Blog-Posts/2020-03-19 v2_3_Release/react-native-ui.png
  63. 34
      docs/zh-Hans/CLI.md
  64. 148
      docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md
  65. 62
      docs/zh-Hans/Customizing-Application-Modules-Guide.md
  66. 166
      docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md
  67. 6
      docs/zh-Hans/Customizing-Application-Modules-Overriding-User-Interface.md
  68. 16
      docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md
  69. 140
      docs/zh-Hans/Modules/Docs.md
  70. 6
      docs/zh-Hans/Multi-Tenancy.md
  71. 91
      docs/zh-Hans/Samples/Microservice-Demo.md
  72. 6
      docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md
  73. 3
      docs/zh-Hans/UI/AspNetCore/Customization-User-Interface.md
  74. 3
      docs/zh-Hans/UI/AspNetCore/Layout-Hooks.md
  75. 3
      docs/zh-Hans/UI/AspNetCore/Navigation-Menu.md
  76. 68
      docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Buttons.md
  77. 27
      docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Index.md
  78. 2
      docs/zh-Hans/UI/AspNetCore/Theming.md
  79. 3
      docs/zh-Hans/UI/AspNetCore/Toolbars.md
  80. 16
      docs/zh-Hans/UI/AspNetCore/Widgets.md
  81. 38
      docs/zh-Hans/docs-nav.json
  82. BIN
      docs/zh-Hans/images/bookstore-added-brand-files.png
  83. BIN
      docs/zh-Hans/images/bookstore-added-logo.png
  84. BIN
      docs/zh-Hans/images/bookstore-added-role-js-file.png
  85. BIN
      docs/zh-Hans/images/bookstore-brand-area-highlighted.png
  86. BIN
      docs/zh-Hans/images/bookstore-global-css-file.png
  87. BIN
      docs/zh-Hans/images/bookstore-google-analytics-view-component.png
  88. BIN
      docs/zh-Hans/images/bookstore-menus-highlighted.png
  89. BIN
      docs/zh-Hans/images/bookstore-notification-icon-on-toolbar.png
  90. BIN
      docs/zh-Hans/images/bookstore-notification-view-component.png
  91. BIN
      docs/zh-Hans/images/bookstore-toolbar-highlighted.png
  92. BIN
      docs/zh-Hans/images/docs-module_download-new-abp-project.png
  93. BIN
      docs/zh-Hans/images/docs-module_solution-explorer.png
  94. BIN
      docs/zh-Hans/images/microservice-sample-diagram-3.png
  95. BIN
      docs/zh-Hans/images/microservice-sample-solution-2.png
  96. BIN
      docs/zh-Hans/images/overriding-login-cshtml.png
  97. 2
      framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj
  98. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelper.cs
  99. 14
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs
  100. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalTagHelper.cs

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

@ -1,7 +1,7 @@
{
"culture": "en",
"texts": {
"Account": "Account",
"Account": "ABP Account - Login & Register | ABP.IO",
"Welcome": "Welcome",
"UseOneOfTheFollowingLinksToContinue": "Use one of the following links to continue",
"FrameworkHomePage": "Framework home page",

1
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -81,6 +81,7 @@
"LastSeenDate": "Last seen date",
"{0}Computer{1}WillBeRemovedFromRecords": "Computer of {0} ({1}) will be removed from records",
"OrganizationDeletionWarningMessage": "Organization will be deleted",
"DeletingLastOwnerWarningMessage": "An organization must have at least one owner! Therefore you cannot remove this owner",
"This{0}AlreadyExistInThisOrganization": "This {0} already exist in this organization",
"AreYouSureYouWantToDeleteAllComputers": "Are you sure you want to delete all computers?",
"DeleteAll": "Delete all",

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

@ -29,7 +29,7 @@
"ApiKey": "API key",
"UserNameNotFound": "There is no user with username {0}",
"SuccessfullyAddedToNewsletter": "Thanks you for subscribing to our newsletter!",
"ManageProfile": "Manage your profile",
"MyProfile": "My profile",
"EmailNotValid": "Please enter a valid email address."
}
}

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

@ -1,7 +1,7 @@
{
"culture": "en",
"texts": {
"GetStarted": "Get Started",
"GetStarted": "Get Started - Startup Templates",
"Create": "Create",
"NewProject": "New Project",
"DirectDownload": "Direct Download",
@ -86,7 +86,7 @@
"BasedOnFamiliarToolsExplanation": "Built on and integrated to popular tools you already know. Low learning curve, easy adaptation, comfortable development.",
"ORMIndependent": "ORM Independent",
"ORMIndependentExplanation": "The core framework is ORM/database independent and can work with any data source. Entity Framework Core and MongoDB providers are already available.",
"Features": "Features",
"Features": "Explore the ABP Framework Features",
"ABPCLI": "ABP CLI",
"Modularity": "Modularity",
"BootstrapTagHelpers": "Bootstrap Tag Helpers",

2
common.props

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

52
docs/en/Application-Services.md

@ -201,7 +201,7 @@ See the [authorization document](Authorization.md) for more.
## CRUD Application Services
If you need to create a simple **CRUD application service** which has Create, Update, Delete and Get methods, you can use ABP's **base classes** to easily build your services. You can inherit from `CrudAppService`.
If you need to create a simple **CRUD application service** which has Create, Update, Delete and Get methods, you can use ABP's **base classes** to easily build your services. You can inherit from the `CrudAppService`.
### Example
@ -219,7 +219,9 @@ public interface IBookAppService :
}
````
* `ICrudAppService` has generic arguments to get the primary key type of the entity and the DTO types for the CRUD operations (it does not get the entity type since the entity type is not exposed to the clients use this interface).
`ICrudAppService` has generic arguments to get the primary key type of the entity and the DTO types for the CRUD operations (it does not get the entity type since the entity type is not exposed to the clients use this interface).
> Creating interface for an application service is a good practice, but not required by the ABP Framework. You can skip the interface part.
`ICrudAppService` declares the following methods:
@ -292,6 +294,52 @@ public class BookAppService :
`CrudAppService` implements all methods declared in the `ICrudAppService` interface. You can then add your own custom methods or override and customize base methods.
> `CrudAppService` has different versions gets different number of generic arguments. Use the one suitable for you.
### AbstractKeyCrudAppService
`CrudAppService` requires to have an Id property as the primary key of your entity. If you are using composite keys then you can not utilize it.
`AbstractKeyCrudAppService` implements the same `ICrudAppService` interface, but this time without making assumption about your primary key.
#### Example
Assume that you have a `District` entity with `CityId` and `Name` as a composite primary key. Using `AbstractKeyCrudAppService` requires to implement `DeleteByIdAsync` and `GetEntityByIdAsync` methods yourself:
````csharp
public class DistrictAppService
: AbstractKeyCrudAppService<District, DistrictDto, DistrictKey>
{
public DistrictAppService(IRepository<District> repository)
: base(repository)
{
}
protected override async Task DeleteByIdAsync(DistrictKey id)
{
await Repository.DeleteAsync(d => d.CityId == id.CityId && d.Name == id.Name);
}
protected override async Task<District> GetEntityByIdAsync(DistrictKey id)
{
return await AsyncQueryableExecuter.FirstOrDefaultAsync(
Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name)
);
}
}
````
This implementation requires you to create a class represents your composite key:
````csharp
public class DistrictKey
{
public Guid CityId { get; set; }
public string Name { get; set; }
}
````
## Lifetime
Lifetime of application services are [transient](Dependency-Injection.md) and they are automatically registered to the dependency injection system.

4
docs/en/Authorization.md

@ -204,6 +204,10 @@ If you define and register a policy to the ASP.NET Core authorization system wit
See [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) document to learn how to define a custom policy.
### Changing Permission Definitions of a Depended Module
A class deriving from the `PermissionDefinitionProvider` (just like the example above) can also get existing permission definitions (defined by the depended [modules](Module-Development-Basics.md)) and change their definitions.
## IAuthorizationService
ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization.

6
docs/en/Background-Jobs.md

@ -171,4 +171,8 @@ Background job system is extensible and you can change the default background jo
See pre-built job manager alternatives:
* [Hangfire Background Job Manager](Background-Jobs-Hangfire.md)
* [RabbitMQ Background Job Manager](Background-Jobs-RabbitMq.md)
* [RabbitMQ Background Job Manager](Background-Jobs-RabbitMq.md)
* [Quartz Background Job Manager](Background-Jobs-Quartz.md)
## See Also
* [Background Workers](Background-Workers.md)

139
docs/en/Background-Workers.md

@ -1,3 +1,140 @@
# Background Workers
TODO
## Introduction
Background workers are simple independent threads in the application running in the background. Generally, they run periodically to perform some tasks. Examples;
* A background worker can run periodically to **delete old logs**.
* A background worker can run periodically to **determine inactive users** and **send emails** to get users to return to your application.
## Create a Background Worker
A background worker should directly or indirectly implement the `IBackgroundWorker` interface.
> A background worker is inherently [singleton](Dependency-Injection.md). So, only a single instance of your worker class is instantiated and run.
### BackgroundWorkerBase
`BackgroundWorkerBase` is an easy way to create a background worker.
````csharp
public class MyWorker : BackgroundWorkerBase
{
public override Task StartAsync(CancellationToken cancellationToken = default)
{
//...
}
public override Task StopAsync(CancellationToken cancellationToken = default)
{
//...
}
}
````
Start your worker in the `StartAsync` (which is called when the application begins) and stop in the `StopAsync` (which is called when the application shuts down).
> You can directly implement the `IBackgroundWorker`, but `BackgroundWorkerBase` provides some useful properties like `Logger`.
### AsyncPeriodicBackgroundWorkerBase
Assume that we want to make a user passive, if the user has not logged in to the application in last 30 days. `AsyncPeriodicBackgroundWorkerBase` class simplifies to create periodic workers, so we will use it for the example below:
````csharp
public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
{
public PassiveUserCheckerWorker(
AbpTimer timer,
IServiceScopeFactory serviceScopeFactory
) : base(
timer,
serviceScopeFactory)
{
Timer.Period = 600000; //10 minutes
}
protected override async Task DoWorkAsync(
PeriodicBackgroundWorkerContext workerContext)
{
Logger.LogInformation("Starting: Setting status of inactive users...");
//Resolve dependencies
var userRepository = workerContext
.ServiceProvider
.GetRequiredService<IUserRepository>();
//Do the work
await userRepository.UpdateInactiveUserStatusesAsync();
Logger.LogInformation("Completed: Setting status of inactive users...");
}
}
````
* `AsyncPeriodicBackgroundWorkerBase` uses the `AbpTimer` (a thread-safe timer) object to determine **the period**. We can set its `Period` property in the constructor.
* It required to implement the `DoWorkAsync` method to **execute** the periodic work.
* It is a good practice to **resolve dependencies** from the `PeriodicBackgroundWorkerContext` instead of constructor injection. Because `AsyncPeriodicBackgroundWorkerBase` uses a `IServiceScope` that is **disposed** when your work finishes.
* `AsyncPeriodicBackgroundWorkerBase` **catches and logs exceptions** thrown by the `DoWorkAsync` method.
## Register Background Worker
After creating a background worker class, you should to add it to the `IBackgroundWorkerManager`. The most common place is the `OnApplicationInitialization` method of your module class:
````csharp
[DependsOn(typeof(AbpBackgroundWorkersModule))]
public class MyModule : AbpModule
{
public override void OnApplicationInitialization(
ApplicationInitializationContext context)
{
context.AddBackgroundWorker<PassiveUserCheckerWorker>();
}
}
````
`context.AddBackgroundWorker(...)` is a shortcut extension method for the expression below:
````csharp
context.ServiceProvider
.GetRequiredService<IBackgroundWorkerManager>()
.Add(
context
.ServiceProvider
.GetRequiredService<PassiveUserCheckerWorker>()
);
````
So, it resolves the given background worker and adds to the `IBackgroundWorkerManager`.
While we generally add workers in `OnApplicationInitialization`, there are no restrictions on that. You can inject `IBackgroundWorkerManager` anywhere and add workers at runtime. Background worker manager will stop and release all the registered workers when your application is being shut down.
## Options
`AbpBackgroundWorkerOptions` class is used to [set options](Options.md) for the background workers. Currently, there is only one option:
* `IsEnabled` (default: true): Used to **enable/disable** the background worker system for your application.
> See the [Options](Options.md) document to learn how to set options.
## Making Your Application Always Run
Background workers only work if your application is running. If you host the background job execution in your web application (this is the default behavior), you should ensure that your web application is configured to always be running. Otherwise, background jobs only work while your application is in use.
## Running On a Cluster
Be careful if you run multiple instances of your application simultaneously in a clustered environment. In that case, every application runs the same worker which may create conflicts if your workers are running on the same resources (processing the same data, for example).
If that's a problem for your workers, you have two options;
* Disable the background worker system using the `AbpBackgroundWorkerOptions` described above, for all the application instances, except one of them.
* Disable the background worker system for all the application instances and create another special application that runs on a single server and execute the workers.
## Quartz Integration
ABP Framework's background worker system is good to implement periodic tasks. However, you may want to use an advanced task scheduler like [Quartz](https://www.quartz-scheduler.net/). See the community contributed [quartz integration](Background-Workers-Quartz.md) for the background workers.
## See Also
* [Quartz Integration for the background workers](Background-Workers-Quartz.md)
* [Background Jobs](Background-Jobs.md)

142
docs/en/Blog-Posts/2020-03-19 v2_3_Release/Post.md

@ -0,0 +1,142 @@
# ABP Framework v2.3.0 Has Been Released!
In the days of **coronavirus**, we have released **ABP Framework v2.3** and this post will explain **what's new** with this release and **what we've done** in the last two weeks.
## About the Coronavirus & Our Team
**We are very sad** about the coronavirus case. As [Volosoft](https://volosoft.com/) team, we have **remote workers** working in their home in different countries. Beginning from the last week, we've **completely started to work remotely** from home including our main office employees.
We believe in and pray for that the humanity will overcome this issue in a short time.
## About the Release Cycle
Beginning from the ABP v2.1.0, we have started to release feature versions once **in two weeks**, on Thursdays. This is the 3rd release after that decision and we see that it works fine for now and improved our agility.
We will continue to release **feature versions** (like v2.4, v2.5) in every two weeks. In addition, we may release **hotfix versions** (like v2.3.1, v2.3.2) whenever needed.
## What's New in ABP Framework v2.3.0
We've completed & merged **[104](https://github.com/abpframework/abp/milestone/30?closed=1) issues and pull requests** with **393 commits** in this two weeks development period.
I will introduce some new features and enhancements introduced with this release.
### React Native Mobile Application
We have finally completed the **react native mobile application**. It currently allows you to **login**, manage your **users** and **tenants**. It utilizes the same setting, authorization and localization systems of the ABP Framework.
A few screenshots from the application:
![mobile-ui](react-native-ui.png)
It doesn't have much functionality but it is a **perfect starting point** for your own mobile application since it is completely integrated to the backend and supports multi-tenancy.
### Angular TypeScript Proxy Generator
It is common to call a REST endpoint in the server from our Angular applications. In this case, we generally create **services** (those have methods for each service method on he server side) and **model objects** (matches to [DTOs](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects) in the server side).
In addition to manually creating such server-interacting services, we could use tools like [NSWAG](https://github.com/RicoSuter/NSwag) to generate service proxies for us. But NSWAG has the following problems we've experienced:
* It generates a **big, single** .ts file which has some problems;
* It get **too large** when your application grows.
* It doesn't fit into the **[modular](https://docs.abp.io/en/abp/latest/Module-Development-Basics) approach** of the ABP framework.
* It creates a bit **ugly code**. We want to have a clean code (just like if we write manually).
* It can not generate the same **method signature** declared in the server side (because swagger.json doesn't exactly reflect the method signature of the backend service). We've created an endpoint that exposes server side method contacts to allow clients generate a better aligned client proxies.
So, we've decided to create an ABP CLI command to automatically generate the typescript client proxies ([#2222](https://github.com/abpframework/abp/issues/2222)) for your REST API developed with the ABP Framework.
It is easy to use. Just run the following command in the **root folder** of the angular application:
````bash
abp generate-proxy
````
It only creates proxies only for your own application's services. It doesn't create proxies for the services of the application modules you're using (by default). There are several options. See the [CLI documentation](https://docs.abp.io/en/abp/latest/CLI).
### CRUD Application Services for Entities with Composite Keys
` CrudAppService ` is a useful base class to create CRUD application services for your entities. But it doesn't support entities with **composite primary keys**. `AbstractKeyCrudAppService` is the new base class that is developed to support entities with composite primary keys. See [the documentation](https://docs.abp.io/en/abp/latest/Application-Services#abstractkeycrudappservice) for more.
### Add Source Code of the Modules
The application startup template comes with some [application modules](https://docs.abp.io/en/abp/latest/Modules/Index) **pre-installed** as **NuGet & NPM packages**. This have a few important advantages:
* You can **easily [upgrade](https://docs.abp.io/en/abp/latest/CLI#update)** these modules when a new version is available.
* Your solution becomes **cleaner**, so you can focus on your own code.
However, when you need to make **major customizations** for a depended module, it is not easy as its source code is in your applications. To solve this problem, we've introduces a new command to the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) that **replaces** NuGet packages with their **source code** in your solution. The usage is simple:
````bash
abp add-module --with-source-code
````
This command adds a module with source code or replaces with its source code if it is already added as package references.
> It is suggested to **save your changes** to your source control system before using this command since it makes a lot of changes in your source code.
In addition, we've documented how to customize depended modules without changing their source code (see the section below). It is suggested to use modules as packages to easily upgrade them in the future.
> Source code of the free modules are licensed under **MIT**, so you can freely change them and add into your solution.
### Switch to Preview
ABP Framework is rapidly evolving and we are frequently releasing new versions. However, if you want to follow it closer, you can use the **daily preview packages**.
We've created an ABP CLI command to easily **update to the latest preview packages** for your solution. Run the following command in the root folder of your solution:
````bash
abp switch-to-preview
````
It will change the versions of all ABP related NuGet and NPM packages. You can **switch back to the latest stable** when you want:
````bash
abp switch-to-stable
````
See the [ABP CLI document](https://docs.abp.io/en/abp/latest/CLI#switch-to-preview) fore more.
### Documentation Improvements
#### Extending/Customizing Depended Application Modules
We've created a huge documentation that explains how to customize a depended module without changing its source code. See [the documentation](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Guide).
In addition to the documentation, we've revised all the modules ([#3166](https://github.com/abpframework/abp/issues/3166)) to make their services easily extensible & customizable.
#### EF Core Migration Guide
We've recently created a guide to explain the migration system that is used by the ABP startup templates. [This guide](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Migrations) also explains how to customize the migration structure, split your modules across multiple databases, reusing a module's table and son on.
#### Migration from the ASP.NET Boilerplate
If you have a solution built on the ASP.NET Boilerplate, we've [created a guide](https://docs.abp.io/en/abp/latest/AspNet-Boilerplate-Migration-Guide) that tries to help you if you want to migrate your solution to the new ABP Framework.
### Some Other Features
#### The Framework
* Add `IRepository.GetAsync` and `IRepository.FindAsync` methods ([#3184](https://github.com/abpframework/abp/issues/3148)).
#### Modules
* Get password & email address of the admin while creating a new tenant, for the tenant management module ([#3088](https://github.com/abpframework/abp/issues/3088)).
* Elastic search integrated full text search for the docs module ([#2901](https://github.com/abpframework/abp/pull/2901)).
* New Quartz background worker module ([#2762](https://github.com/abpframework/abp/issues/2762))
#### Samples
* Add multi-tenancy support to the microservice demo ([#3032](https://github.com/abpframework/abp/pull/3032)).
See [the release notes](https://github.com/abpframework/abp/releases/tag/2.3.0) for all feature, enhancement and bugfixes.
## What's Next?
We have the following goals for the next few months:
* Complete the **documentation and samples**, write more tutorials.
* Make the framework and existing modules more **customizable and extensible**.
* Integrate to **gRPC** & implement gRPC endpoint for pre-built modules ([#2882](https://github.com/abpframework/abp/issues/2882)).
* Create a **Blazor UI** for the ABP Framework & implement it for all the modules and startup templates ([#394](https://github.com/abpframework/abp/issues/394)).
* Add **new features** to pre-built modules and create new modules for the [ABP Commercial](https://commercial.abp.io/).
See [the GitHub milestones](https://github.com/abpframework/abp/milestones) for details.

BIN
docs/en/Blog-Posts/2020-03-19 v2_3_Release/abp-io-release-2-3-0-blog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

BIN
docs/en/Blog-Posts/2020-03-19 v2_3_Release/react-native-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

39
docs/en/CLI.md

@ -22,7 +22,7 @@ dotnet tool update -g Volo.Abp.Cli
Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md).
Basic usage:
Usage:
````bash
abp new <solution-name> [options]
@ -48,7 +48,10 @@ abp new Acme.BookStore
* `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side.
* `none`: Without UI. There are some additional options for this template:
* `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `--mobile` or `-m`: Specifies the mobile application framework. Default framework is `react-native`. Available frameworks:
* `none`: no mobile application.
* `react-native`: React Native.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `module`: [Module template](Startup-Templates/Module.md). Additional options:
@ -56,6 +59,7 @@ abp new Acme.BookStore
* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory.
* `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version.
* `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D\localTemplate` or `https://<your url>.zip`).
* `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder.
### add-package
@ -90,7 +94,7 @@ Adds a [multi-package application module](Modules/Index) to a solution by findin
> A business module generally consists of several packages (because of layering, different database provider options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module.
Basic usage:
Usage
````bash
abp add-module <module-name> [options]
@ -141,7 +145,7 @@ abp switch-to-preview [options]
````
#### Options
`--solution-path` or `-sp`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
`--solution-directory` or `-sd`: Specifies the directory. The solution should be in that directory or in any of its sub directories. If not specified, default is the current directory.
### switch-to-stable
@ -154,7 +158,7 @@ abp switch-to-stable [options]
````
#### Options
`--solution-path` or `-sp`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
`--solution-directory` or `-sd`: Specifies the directory. The solution should be in that directory or in any of its sub directories. If not specified, default is the current directory.
### login
@ -174,6 +178,29 @@ Logs you out by removing the session token from your computer.
abp logout
```
### generate-proxy
Generates client proxies for your HTTP APIs to make easy to consume your services from the client side. Before running `generate-proxy` command, your host must be up and running.
Usage:
````bash
abp generate-proxy [options]
````
#### Options
* `--apiUrl` or `-a`: Specifies the root URL of the HTTP API. The default value is being retrieved from the `environment.ts` file for the Angular application. Make sure your host is up and running before running `abp generate-proxy`.
* `--ui` or `-u`: Specifies the UI framework. Default value is `angular` and it is the only UI framework supported for now. Creates TypeScript code.
* `--module` or `-m`: Specifies the module name. Default module name is `app`, which indicates your own application (you typically want this since every module is responsible to maintain its own client proxies). Set `all` for to generate proxies for all the modules.
Example usage with the options:
````bash
abp generate-proxy --apiUrl https://localhost:44305 --ui angular --module all
````
### help
Writes basic usage information of the CLI.
@ -189,4 +216,4 @@ Examples:
````bash
abp help # Shows a general help.
abp help new # Shows help about the "new" command.
````
````

9
docs/en/Customizing-Application-Modules-Guide.md

@ -21,7 +21,7 @@ This approach has the following benefits:
However, there is a drawback:
* You may not able to **customize** the module source code as it is in your own solution.
* You may not able to **customize** the module because the module source is not in your solution.
This document explains **how to customize or extend** a depended module without need to change its source code. While it is limited compared to a full source code change opportunity, there are still some good ways to make some customizations.
@ -53,3 +53,10 @@ This section suggests some approaches if you decided to use pre-built applicatio
* [Overriding Services](Customizing-Application-Modules-Overriding-Services.md)
* [Overriding the User Interface](Customizing-Application-Modules-Overriding-User-Interface.md)
### See Also
Also, see the following documents:
* See [the localization document](Localization.md) to learn how to extend existing localization resources.
* See [the settings document](Settings.md) to learn how to change setting definitions of a depended module.
* See [the authorization document](Authorization.md) to learn how to change permission definitions of a depended module.

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

@ -10,7 +10,7 @@ You have different options can be used based on your requirement those will be e
## Replacing an Interface
If given service defines an interface, like the `IdentityUserAppService` class implements the `IIdentityAppService`, you can re-implement the same interface and replace the current implementation by your class. Example:
If given service defines an interface, like the `IdentityUserAppService` class implements the `IIdentityUserAppService`, you can re-implement the same interface and replace the current implementation by your class. Example:
````csharp
public class MyIdentityUserAppService : IIdentityUserAppService, ITransientDependency

5
docs/en/Customizing-Application-Modules-Overriding-User-Interface.md

@ -3,7 +3,4 @@
You may want to override a page, a component, a JavaScript, CSS or an image file of your depended module. Overriding the UI completely depends on the UI framework you're using. Select the UI framework to continue:
* [ASP.NET Core (MVC / Razor Pages)](UI/AspNetCore/Customization-User-Interface.md)
* [Angular](UI/Angular/Customization-User-Interface.md)
* [Angular](UI/Angular/Customization-User-Interface.md)

16
docs/en/Entity-Framework-Core-Other-DBMS.md

@ -91,4 +91,18 @@ 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.
## DBMS restrictions
Different DBMS may have some restrictions, such as the maximum length of field names, index length, etc.
The module may provide some built-in solutions. You can configure it via `ModelBuilder`. eg: `Identity Server` module.
```csharp
builder.ConfigureIdentityServer(options =>
{
options.DatabaseProvider = EfCoreDatabaseProvider.MySql;
});
```
Related discussions: https://github.com/abpframework/abp/issues/1920

28
docs/en/Exception-Handling.md

@ -1,4 +1,4 @@
## Exception Handling
# Exception Handling
ABP provides a built-in infrastructure and offers a standard model for handling exceptions in a web application.
@ -7,7 +7,7 @@ ABP provides a built-in infrastructure and offers a standard model for handling
* Provides a configurable way to **localize** exception messages.
* Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map these to custom exceptions.
### Automatic Exception Handling
## Automatic Exception Handling
`AbpExceptionFilter` handles an exception if **any of the following conditions** are met:
@ -17,7 +17,7 @@ ABP provides a built-in infrastructure and offers a standard model for handling
If the exception is handled it's automatically **logged** and a formatted **JSON message** is returned to the client.
#### Error Message Format
### Error Message Format
Error Message is an instance of the `RemoteServiceErrorResponse` class. The simplest error JSON has a **message** property as shown below:
@ -83,11 +83,11 @@ Error **details** in an optional field of the JSON error message. Thrown `Except
`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have higly customised validation logic.
#### Logging
### Logging
Caught exceptions are automatically logged.
##### Log Level
#### Log Level
Exceptions are logged with the `Error` level by default. The Log level can be determined by the exception if it implements the `IHasLogLevel` interface. Example:
@ -100,7 +100,7 @@ public class MyException : Exception, IHasLogLevel
}
````
##### Self Logging Exceptions
#### Self Logging Exceptions
Some exception types may need to write additional logs. They can implement the `IExceptionWithSelfLogging` if needed. Example:
@ -116,7 +116,7 @@ public class MyException : Exception, IExceptionWithSelfLogging
> `ILogger.LogException` extension methods is used to write exception logs. You can use the same extension method when needed.
### Business Exceptions
## Business Exceptions
Most of your own exceptions will be business exceptions. The `IBusinessException` interface is used to mark an exception as a business exception.
@ -145,11 +145,11 @@ Volo.Qa:010002
* You can **directly throw** a `BusinessException` or **derive** your own exception types from it when needed.
* All properties are optional for the `BusinessException` class. But you generally set either `ErrorCode` or `Message` property.
### Exception Localization
## Exception Localization
One problem with throwing exceptions is how to localize error messages while sending it to the client. ABP offers two models and their variants.
#### User Friendly Exception
### User Friendly Exception
If an exception implements the `IUserFriendlyException` interface, then ABP does not change it's `Message` and `Details` properties and directly send it to the client.
@ -192,7 +192,7 @@ Then the localization text can be:
* The `IUserFriendlyException` interface is derived from the `IBusinessException` and the `UserFriendlyException` class is derived from the `BusinessException` class.
#### Using Error Codes
### Using Error Codes
`UserFriendlyException` is fine, but it has a few problems in advanced usages:
@ -230,7 +230,7 @@ throw new BusinessException(QaDomainErrorCodes.CanNotVoteYourOwnAnswer);
* Throwing any exception implementing the `IHasErrorCode` interface behaves the same. So, the error code localization approach is not unique to the `BusinessException` class.
* Defining localized string is not required for an error message. If it's not defined, ABP sends the default error message to the client. It does not use the `Message` property of the exception! if you want that, use the `UserFriendlyException` (or use an exception type that implements the `IUserFriendlyException` interface).
##### Using Message Parameters
#### Using Message Parameters
If you have a parameterized error message, then you can set it with the exception's `Data` property. For example:
@ -265,7 +265,7 @@ Then the localized text can contain the `UserName` parameter:
* `WithData` can be chained with more than one parameter (like `.WithData(...).WithData(...)`).
### HTTP Status Code Mapping
## HTTP Status Code Mapping
ABP tries to automatically determine the most suitable HTTP status code for common exception types by following these rules:
@ -280,7 +280,7 @@ ABP tries to automatically determine the most suitable HTTP status code for comm
The `IHttpExceptionStatusCodeFinder` is used to automatically determine the HTTP status code. The default implementation is the `DefaultHttpExceptionStatusCodeFinder` class. It can be replaced or extended as needed.
#### Custom Mappings
### Custom Mappings
Automatic HTTP status code determination can be overrided by custom mappings. For example:
@ -291,7 +291,7 @@ services.Configure<AbpExceptionHttpStatusCodeOptions>(options =>
});
````
### Built-In Exceptions
## Built-In Exceptions
Some exception types are automatically thrown by the framework:

3
docs/en/IdentityServer-Integration.md

@ -0,0 +1,3 @@
# IdentityServer Integration
TODO

2
docs/en/Modules/Blogging.md

@ -1,3 +1,3 @@
# IdentityServer Module
# Blogging Module
TODO

160
docs/en/Modules/Docs.md

@ -20,23 +20,25 @@ When you use GitHub to store your docs, Docs Module supports versioning. If you
> Docs module follows the [module architecture best practices](../Best-Practices/Module-Architecture.md) guide.
## Installation
### 1- Download
## Installation
If you do not have an existing ABP project, this step shows you how to create a new project from [abp.io](https://abp.io) to add the Docs Module. If you already have an ABP project, you can skip this step.
### 1- Download
It is recommended to use ABP CLI to create new projects. Use the following command:
If you do not have an existing ABP project, this step shows you how to create a new project from [abp.io](https://abp.io) to add the Docs Module. If you already have an ABP project, you can skip this step.
`abp new Acme.MyProject`
Navigate to https://abp.io/Templates. Enter your project name as `Acme.MyProject`, select `ASP.NET Core Mvc Application` and select `Entity Framework Core` for the database provider.
You can also navigate to https://abp.io/get-started. Enter your project name as `Acme.MyProject`, other use default options.
Note that this document covers `Entity Framework Core` provider but you can also select `MongoDB` as your database provider.
Note that this document covers `Entity Framework Core` provider but you can also select `MongoDB` as your database provider.
![Create new project](../images/docs-module_download-new-abp-project.png)
### 2- Running The Empty Application
After you download the project, extract the ZIP file and open `Acme.MyProject.sln`. You will see that the solution consists of `Application`, `Domain `, `EntityFrameworkCore` and `Web` projects. Right click on `Acme.MyProject.Web` project and **Set as StartUp Project**.
After you download the project, extract the ZIP file and open `Acme.MyProject.sln`. You will see that the solution consists of `Application`, `Application.Contracts`, `DbMigrator`, `Domain`, `Domain.Shared`, `EntityFrameworkCore`, `EntityFrameworkCore.DbMigations`, `HttpApi`, `HttpApi.Client` and `Web` projects. Right click on `Acme.MyProject.Web` project and **Set as StartUp Project**.
![Create a new project](../images/docs-module_solution-explorer.png)
@ -45,16 +47,14 @@ The database connection string is located in `appsettings.json` of your `Acme.My
```json
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true"
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
```
Run `Acme.MyProject.DbMigrator` project, it will be responsible for applying database migration and seed data. The database `MyProject` will be created in your database server.
Open `Package Manager Console` in the Visual Studio and choose `src\Acme.MyProject.EntityFrameworkCore` as the default project. Run `Update-Database` command to create your new database. The database `MyProject` will be created in your database server.
Now an empty ABP project has been created! You can now run your project and see the empty website.
Now an empty ABP project has been created! You can now run your project and see the empty website.
To login your website enter `admin` as the username and `1q2w3E*` as the password.
@ -62,36 +62,27 @@ To login your website enter `admin` as the username and `1q2w3E*` as the passwor
Docs module packages are hosted on NuGet. There are 4 packages that needs be to installed to your application. Each package has to be installed to the relevant project.
* [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) needs to be referenced to `Acme.MyProject.Domain` project.
It is recommended to use the ABP CLI to install the module, open the CMD window in the solution file (`.sln`) directory, and run the following command:
* Edit `Acme.MyProject.Domain.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest.
`abp add-module Volo.Docs`
```csharp
<PackageReference Include="Volo.Docs.Domain" Version="0.9.0" />
```
* [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) needs to be referenced to `Acme.MyProject.EntityFrameworkCore` project.
Or you can also manually install nuget package to each project:
- Edit `Acme.MyProject.EntityFrameworkCore.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest.
* Install [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Domain` project.
```csharp
<PackageReference Include="Volo.Docs.EntityFrameworkCore" Version="0.9.0" />
```
* [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) needs to be referenced to `Acme.MyProject.Application` project.
`Install-Package Volo.Docs.Domain`
* Edit `Acme.MyProject.Application.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest.
* Install [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) nuget package to `Acme.MyProject.EntityFrameworkCore` project.
```csharp
<PackageReference Include="Volo.Docs.Application" Version="0.9.0" />
```
* [Volo.Docs.Web ](https://www.nuget.org/packages/Volo.Docs.Web/)needs to be referenced to `Acme.MyProject.Web` project.
`Install-Package Volo.Docs.EntityFrameworkCore`
- Edit `Acme.MyProject.Web.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest.
* Install [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) nuget package to `Acme.MyProject.Application` project.
```csharp
<PackageReference Include="Volo.Docs.Web" Version="0.9.0" />
```
`Install-Package Volo.Docs.Application`
* Install [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Web` project.
`Install-Package Volo.Docs.Web`
### 3- Adding Module Dependencies
@ -132,7 +123,6 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a
}
```
* Open `MyProjectApplicationModule.cs`and add `typeof(DocsApplicationModule)` as shown below;
```csharp
@ -157,7 +147,6 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a
}
```
* Open `MyProjectWebModule.cs`and add `typeof(DocsWebModule)` as shown below;
```csharp
@ -176,43 +165,62 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a
}
```
### 4- Database Integration
#### 4.1- Entity Framework Integration
If you choose Entity Framework as your database provider, you need to configure the Docs Module in your DbContext. To do this;
If you choose Entity Framework as your database provider, you need to configure the Docs Module. To do this;
- Open `MyProjectDbContext.cs` and add `modelBuilder.ConfigureDocs()` to the `OnModelCreating()`
- Open `MyProjectMigrationsDbContext.cs` and add `builder.ConfigureDocs()` to the `OnModelCreating()`.
```csharp
[ConnectionStringName("Default")]
public class MyProjectDbContext : AbpDbContext<MyProjectDbContext>
{
public MyProjectDbContext(DbContextOptions<MyProjectDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//...
modelBuilder.ConfigureDocs();
}
}
public class MyProjectMigrationsDbContext : AbpDbContext<MyProjectMigrationsDbContext>
{
public MyProjectMigrationsDbContext(DbContextOptions<MyProjectMigrationsDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureBackgroundJobs();
builder.ConfigureAuditLogging();
builder.ConfigureIdentity();
builder.ConfigureIdentityServer();
builder.ConfigureFeatureManagement();
builder.ConfigureTenantManagement();
builder.ConfigureDocs(); //Add this line to configure the Docs Module
/* Configure customizations for entities from the modules included */
builder.Entity<IdentityUser>(b =>
{
b.ConfigureCustomUserProperties();
});
/* Configure your own tables/entities inside the ConfigureQaDoc method */
builder.ConfigureMyProject();
}
}
```
* Open `Package Manager Console` in `Visual Studio` and choose `Acme.MyProject.EntityFrameworkCore` as default project. Then write the below command to add the migration for Docs Module.
* Open `Package Manager Console` in `Visual Studio` and choose `Acme.MyProject.EntityFrameworkCore.DbMigrations` as default project. Then write the below command to add the migration for Docs Module.
```csharp
add-migration Added_Docs_Module
```
When the command successfully executes , you will see a new migration file named as `20181221111621_Added_Docs_Module` in the folder `Acme.MyProject.EntityFrameworkCore\Migrations`.
When the command successfully executes , you will see a new migration file named as `20181221111621_Added_Docs_Module` in the folder `Acme.MyProject.EntityFrameworkCore.DbMigrations\Migrations`.
Now, update the database for Docs module database changes. To do this run the below code on `Package Manager Console` in `Visual Studio`. Be sure `Acme.MyProject.EntityFrameworkCore` is still default project.
Now, update the database for Docs module database changes. To do this run the below code on `Package Manager Console` in `Visual Studio`. Be sure `Acme.MyProject.EntityFrameworkCore.DbMigrations` is still default project.
```csharp
update-database
@ -220,7 +228,6 @@ If you choose Entity Framework as your database provider, you need to configure
Finally, you can check your database to see the newly created tables. For example you can see `DocsProjects` table must be added to your database.
### 5- Linking Docs Module
The default route for Docs module is;
@ -250,7 +257,7 @@ To add Docs module link to your application menu;
}
```
The `Menu:Docs` keyword is a localization key. To localize the menu text, open `Localization\MyProject\en.json` in the project `Acme.MyProject.Domain`. And add the below line
The `Menu:Docs` keyword is a localization key. To localize the menu text, open `Localization\MyProject\en.json` in the project `Acme.MyProject.Domain`. And add the below line
```json
"Menu:Docs": "Documents"
@ -270,7 +277,7 @@ Final look of **en.json**
}
```
The new menu item for Docs Module is added to the menu. Run your web application and browse to `http://localhost:YOUR_PORT_NUMBER/documents` URL.
The new menu item for Docs Module is added to the menu. Run your web application and browse to `http://localhost:YOUR_PORT_NUMBER/documents` URL.
You will see a warning says;
@ -316,23 +323,25 @@ You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documen
- ExtraProperties:
```json
{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***"}
{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***","GitHubUserAgent":""}
```
Note that `GitHubAccessToken` is masked with `***`. It's a private token that you must get it from GitHub. See https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
- MainWebsiteUrl: `/`
- MainWebsiteUrl: `/`
- LatestVersionBranchName: `master`
For `SQL` databases, you can use the below `T-SQL` command to insert the specified sample into your `DocsProjects` table:
```mssql
INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***"}', N'/', N'master', N'')
INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***","GitHubUserAgent":""}', N'/', N'master', N'')
```
Be aware that `GitHubAccessToken` is masked. It's a private token and you must get your own token and replace the `***` string.
Now you can run the application and navigate to `/Documents`.
#### Sample Project Record for "FileSystem"
You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documents to configure your GitHub document store.
@ -561,11 +570,34 @@ The upper sample `JSON` file renders the below navigation menu as `HTML`.
![Navigation menu](../images/docs-module_download-sample-navigation-menu.png)
Finally a new Docs Module is added to your project which is feeded with GitHub.
Finally a new Docs Module is added to your project which is feeded with GitHub.
## Full-Text Search(Elastic Search)
## Next
The Docs module supports full-text search using Elastic Search. It is not enabled by default. You can configure `DocsElasticSearchOptions` to enable it.
```
Configure<DocsElasticSearchOptions>(options =>
{
options.Enable = true;
options.IndexName = "your_index_name"; //default IndexName is abp_documents
});
```
The `Index` is automatically created after the application starts if the `Index` does not exist.
Docs Module is also available as a standalone application. Check out [VoloDocs](../Apps/VoloDocs).
`DefaultElasticClientProvider` is responsible for creating `IElasticClient`. By default, it reads Elastic Search's `Url` from `IConfiguration`.
If your `IElasticClient` needs additional configuration, please use override `IElasticClientProvider` service and replace it in the [dependency injection](Dependency-Injection.md) system.
```
{
"ElasticSearch": {
"Url": "http://localhost:9200"
}
}
```
## Next
Docs Module is also available as a standalone application. Check out [VoloDocs](../Apps/VoloDocs).

12
docs/en/Multi-Tenancy.md

@ -4,7 +4,7 @@ ABP Multi-tenancy module provides base functionality to create multi tenant appl
Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) multi-tenancy as like that:
> Software **Multi-tenancy** refers to a software **architecture** in which a **single instance** of a software runs on a server and serves **multiple tenants**. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a **dedicated share of the instance including its data**, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants.
> Software **Multi-tenancy** refers to a software **architecture** in which a **single instance** of software runs on a server and serves **multiple tenants**. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a **dedicated share of the instance including its data**, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants.
### Volo.Abp.MultiTenancy Package
@ -302,7 +302,7 @@ TODO:...
Volo.Abp.AspNetCore.MultiTenancy package adds following tenant resolvers to determine current tenant from current web request (ordered by priority). These resolvers are added and work out of the box:
* **CurrentUserTenantResolveContributor**: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be stay as the first contributor for security**.
* **CurrentUserTenantResolveContributor**: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for security**.
* **QueryStringTenantResolver**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default.
* **RouteTenantResolver**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route.
* **HeaderTenantResolver**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default.
@ -343,8 +343,10 @@ namespace MyCompany.MyProject
{
Configure<AbpTenantResolveOptions>(options =>
{
//Subdomain format: {0}.mydomain.com (adding as the highest priority resolver)
options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));
//Subdomain format: {0}.mydomain.com
//Adding as the second highest priority resolver after 'CurrentUserTenantResolveContributor' to
//ensure the user cannot impersonate a different tenant.
options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));
});
//...
@ -355,7 +357,7 @@ namespace MyCompany.MyProject
{0} is the the placeholder to determine current tenant's unique name.
Instead of ``options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));`` you can use this shortcut:
Instead of ``options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));`` you can use this shortcut:
````C#
options.AddDomainTenantResolver("{0}.mydomain.com");

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

@ -641,7 +641,6 @@ Open `book-list.component.html` file in `books\book-list` folder and replace the
</div>
<div class="card-body">
<abp-table
*ngIf="[150, 0] as columnWidths"
[value]="books$ | async"
[abpLoading]="loading"
[headerTemplate]="tableHeader"

0
docs/en/Tutorials/part-3.md → docs/en/Tutorials/Part-3.md

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

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

8
docs/en/UI/Angular/Custom-Setting-Page.md

@ -1,6 +1,6 @@
# Custom Setting Page
There are several settings tabs from different modules. You can add custom settings tabs to your project in 3 steps.
There are several settings tabs from different modules. You can add custom settings page to your project in 3 steps.
1. Create a Component
@ -37,6 +37,10 @@ ngOnInit() {
}
```
Open the `setting-management` page to see the changes:
Navigate to `/setting-management` route to see the changes:
![Custom Settings Tab](./images/custom-settings.png)
## What's Next?
- [TrackByService](./Track-By-Service.md)

209
docs/en/UI/Angular/Http-Requests.md

@ -0,0 +1,209 @@
# How to Make HTTP Requests
## About HttpClient
Angular has the amazing [HttpClient](https://angular.io/guide/http) for communication with backend services. It is a layer on top and a simplified representation of [XMLHttpRequest Web API](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). It also is the recommended agent by Angular for any HTTP request. There is nothing wrong with using the `HttpClient` in your ABP project.
However, `HttpClient` leaves error handling to the caller (method). In other words, HTTP errors are handled manually and by hooking into the observer of the `Observable` returned.
```js
getConfig() {
this.http.get(this.configUrl).subscribe(
config => this.updateConfig(config),
error => {
// Handle error here
},
);
}
```
Although clear and flexible, handling errors this way is repetitive work, even when error processing is delegated to the store or any other injectable.
An `HttpInterceptor` is able to catch `HttpErrorResponse`  and can be used for a centralized error handling. Nevertheless, cases where default error handler, therefore the interceptor, must be disabled require additional work and comprehension of Angular internals. Check [this issue](https://github.com/angular/angular/issues/20203) for details.
## RestService
ABP core module has a utility service for HTTP requests: `RestService`. Unless explicitly configured otherwise, it catches HTTP errors and dispatches a `RestOccurError` action. This action is then captured by the `ErrorHandler` introduced by the `ThemeSharedModule`. Since you should already import this module in your app, when the `RestService` is used, all HTTP errors get automatically handled by deafult.
### Getting Started with RestService
In order to use the `RestService`, you must inject it in your class as a dependency.
```js
import { RestService } from '@abp/ng.core';
@Injectable({
/* class metadata here */
})
class DemoService {
constructor(private rest: RestService) {}
}
```
You do not have to provide the `RestService` at module or component/directive level, because it is already **provided in root**.
### How to Make a Request with RestService
You can use the `request` method of the `RestService` is for HTTP requests. Here is an example:
```js
getFoo(id: number) {
const request: Rest.Request<null> = {
method: 'GET',
url: '/api/some/path/to/foo/' + id,
};
return this.rest.request<null, FooResponse>(request);
}
```
The `request` method always returns an `Observable<T>`. Therefore you can do the following wherever you use `getFoo` method:
```js
doSomethingWithFoo(id: number) {
this.demoService.getFoo(id).subscribe(
foo => {
// Do something with foo.
}
)
}
```
**You do not have to worry about unsubscription.** The `RestService` uses `HttpClient` behind the scenes, so every observable it returns is a finite observable, i.e. it closes subscriptions automatically upon success or error.
As you see, `request` method gets a request options object with `Rest.Request<T>` type. This generic type expects the interface of the request body. You may pass `null` when there is no body, like in a `GET` or a `DELETE` request. Here is an example where there is one:
```js
postFoo(body: Foo) {
const request: Rest.Request<Foo> = {
method: 'POST',
url: '/api/some/path/to/foo',
body
};
return this.rest.request<Foo, FooResponse>(request);
}
```
You may [check here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L23) for complete `Rest.Request<T>` type, which has only a few chages compared to [HttpRequest](https://angular.io/api/common/http/HttpRequest) class in Angular.
### How to Disable Default Error Handler of RestService
The `request` method, used with defaults, always handles errors. Let's see how you can change that behavior and handle errors yourself:
```js
deleteFoo(id: number) {
const request: Rest.Request<null> = {
method: 'DELETE',
url: '/api/some/path/to/foo/' + id,
};
return this.rest.request<null, void>(request, { skipHandleError: true });
}
```
`skipHandleError` config option, when set to `true`, disables the error handler and the returned observable starts throwing an error that you can catch in your subscription.
```js
removeFooFromList(id: number) {
this.demoService.deleteFoo(id).subscribe(
foo => {
// Do something with foo.
},
error => {
// Do something with error.
}
)
}
```
### How to Get a Specific API Endpoint From Application Config
Another nice config option that `request` method receives is `apiName` (available as of v2.4), which can be used to get a specific module endpoint from application configuration.
```js
putFoo(body: Foo, id: string) {
const request: Rest.Request<Foo> = {
method: 'PUT',
url: '/' + id,
body
};
return this.rest.request<Foo, void>(request, {apiName: 'foo'});
}
```
`putFoo` above will request `https://localhost:44305/api/some/path/to/foo/{id}` as long as the environment variables are as follows:
```js
// environment.ts
export const environment = {
apis: {
default: {
url: 'https://localhost:44305',
},
foo: {
url: 'https://localhost:44305/api/some/path/to/foo',
},
},
/* rest of the environment variables here */
}
```
### How to Observe Response Object or HTTP Events Instead of Body
`RestService` assumes you are generally interested in the body of a response and, by default, sets `observe` property as `'body'`. However, there may be times you are rather interested in something else, such as a custom proprietary header. For that, the `request` method receives `observe` property in its config object.
```js
getSomeCustomHeaderValue() {
const request: Rest.Request<null> = {
method: 'GET',
url: '/api/some/path/that/sends/some-custom-header',
};
return this.rest.request<null, HttpResponse<any>>(
request,
{observe: Rest.Observe.Response},
).pipe(
map(response => response.headers.get('Some-Custom-Header'))
);
}
```
You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10).
## What's Next?
* [Localization](./Localization.md)

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

@ -133,4 +133,8 @@ Localization resources are stored in the `localization` property of `ConfigState
## See Also
* [Localization in ASP.NET Core](../../Localization.md)
* [Localization in ASP.NET Core](../../Localization.md)
## What's Next?
* [Permission Management](./Permission-Management.md)

114
docs/en/UI/Angular/Track-By-Service.md

@ -0,0 +1,114 @@
# Easy TrackByFunction Implementation
`TrackByService` is a utility service to provide an easy implementation for one of the most frequent needs in Angular templates: `TrackByFunction`. Please see [this page in Angular docs](https://angular.io/guide/template-syntax#ngfor-with-trackby) for its purpose.
## Getting Started
You do not have to provide the `TrackByService` at module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components. For better type support, you may pass in the type of the iterated item to it.
```js
import { TrackByService } from '@abp/ng.core';
@Component({
/* class metadata here */
})
class DemoComponent {
list: Item[];
constructor(public readonly track: TrackByService<Item>) {}
}
```
> Noticed `track` is `public` and `readonly`? That is because we will see some examples where methods of `TrackByService` instance are directly called in the component's template. That may be considered as an anti-pattern, but it has its own advantage, especially when component inheritance is leveraged. You can always use public component properties instead.
**The members are also exported as separate functions.** If you do not want to inject `TrackByService`, you can always import and use those functions directly in your classes.
## Usage
There are two approaches available.
1. You may inject `TrackByService` to your component and use its members.
2. You may use exported higher-order functions directly on component properties.
### How to Track Items by a Key
You can use `by` to get a `TrackByFunction` that tracks the iterated object based on one of its keys. For type support, you may pass in the type of the iterated item to it.
```html
<!-- template of DemoComponent -->
<div *ngFor="let item of list; trackBy: track.by('id')">{%{{{ item.name }}}%}</div>
```
`by` is exported as a stand-alone function and is named `trackBy`.
```js
import { trackBy } from "@abp/ng.core";
@Component({
template: `
<div
*ngFor="let item of list; trackBy: trackById"
>
{%{{{ item.name }}}%}
</div>
`,
})
class DemoComponent {
list: Item[];
trackById = trackBy<Item>('id');
}
```
### How to Track by a Deeply Nested Key
You can use `byDeep` to get a `TrackByFunction` that tracks the iterated object based on a deeply nested key. For type support, you may pass in the type of the iterated item to it.
```html
<!-- template of DemoComponent -->
<div
*ngFor="let item of list; trackBy: track.byDeep('tenant', 'account', 'id')"
>
{%{{{ item.tenant.name }}}%}
</div>
```
`byDeep` is exported as a stand-alone function and is named `trackByDeep`.
```js
import { trackByDeep } from "@abp/ng.core";
@Component({
template: `
<div
*ngFor="let item of list; trackBy: trackByTenantAccountId"
>
{%{{{ item.name }}}%}
</div>
`,
})
class DemoComponent {
list: Item[];
trackByTenantAccountId = trackByDeep<Item>('tenant', 'account', 'id');
}
```

12
docs/en/UI/AspNetCore/Bundling-Minification.md

@ -51,7 +51,7 @@ This bundle defines a style bundle with a **unique name**: `MyGlobalBundle`. It'
* ABP creates the bundle as **lazy** from the provided files when it's **first requested**. For the subsequent calls, it's returned from the **cache**. That means if you conditionally add the files to the bundle, it's executed only once and any changes of the condition will not effect the bundle for the next requests.
* ABP adds bundle files **individually** to the page for the `development` environment. It automatically bundles & minifies for other environments (`staging`, `production`...).
* The bundle files may be **physical** files or [**virtual/embedded** files](../Virtual-File-System.md).
* The bundle files may be **physical** files or [**virtual/embedded** files](../../Virtual-File-System.md).
* ABP automatically adds **version query string** to the bundle file URL to prevent browsers from caching when the bundle is being updated. (like ?_v=67872834243042 - generated from last change date of the related files). The versioning works even if the bundle files are individually added to the page (on the development environment).
### Importing The Bundling Tag Helpers
@ -102,7 +102,7 @@ The bundle name will be *scripts.my-scripts* for the example above ("/" is repla
## Bundling Options
If you need to use same bundle in **multiple pages** or want to use some more **powerful features**, you can configure bundles **by code** in your [module](../Module-Development-Basics.md) class.
If you need to use same bundle in **multiple pages** or want to use some more **powerful features**, you can configure bundles **by code** in your [module](../../Module-Development-Basics.md) class.
### Creating A New Bundle
@ -143,7 +143,7 @@ This time, no file defined in the tag helper definition because the bundle files
### Configuring An Existing Bundle
ABP supports [modularity](../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a depended module. Example:
ABP supports [modularity](../../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a depended module. Example:
````C#
[DependsOn(typeof(MyWebModule))]
@ -159,7 +159,7 @@ public class MyWebExtensionModule : AbpModule
bundle.AddFiles(
"/scripts/my-extension-script.js"
);
});
});
});
}
}
@ -195,7 +195,7 @@ services.Configure<AbpBundlingOptions>(options =>
.ScriptBundles
.Configure("MyGlobalBundle", bundle => {
bundle.AddContributors(typeof(MyExtensionGlobalStyleContributor));
});
});
});
````
@ -327,7 +327,7 @@ services.Configure<AbpBundlingOptions>(options =>
.AddFiles(
"/styles/mytheme-global-styles.css"
);
});
});
});
````

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

@ -9,7 +9,7 @@ This section covers the [Razor Pages](https://docs.microsoft.com/en-us/aspnet/co
You typically have three kind of override requirement for a page:
* Overriding **only the Page Model** (C#) side to perform additional logic without changing the page UI.
* Overring **only the Razor Page** (.chtml file) to change the UI without changing the c# behind the page.
* Overriding **only the Razor Page** (.chtml file) to change the UI without changing the c# behind the page.
* **Completely overriding** the page.
### Overriding a Page Model (C#)
@ -122,19 +122,19 @@ That's all! Make any change in the view and run your application.
#### Replacing Page Model Without Inheritance
You don't have to inherit from the original page model class (like done in the previous example). Instead, you can completely re-implement the page yourself. In this case, just derive from `PageModel`, `AbpPageModel` or any suitable base class you need.
You don't have to inherit from the original page model class (like done in the previous example). Instead, you can completely **re-implement** the page yourself. In this case, just derive from `PageModel`, `AbpPageModel` or any suitable base class you need.
## Overriding a View Component
The ABP Framework, pre-built themes and modules define some re-usable view components. These view components can be replaced just like a page described above.
The ABP Framework, pre-built themes and modules define some **re-usable view components**. These view components can be replaced just like a page described above.
### Example
The screenshot below was taken from the basic theme comes with the application startup template.
The screenshot below was taken from the **basic theme** comes with the application startup template.
![bookstore-brand-area-highlighted](../../images/bookstore-brand-area-highlighted.png)
[The basic theme](../../Themes/Basic.md) defines some view components for the layout. For example, the highlighted area with the red rectangle above is called Brand component. You probably want to customize this component by adding your own application logo. Let's see how to do it.
[The basic theme](../../Themes/Basic.md) defines some view components for the layout. For example, the highlighted area with the red rectangle above is called **Brand component**. You probably want to customize this component by adding your **own application logo**. Let's see how to do it.
First, create your logo and place under a folder in your web application. We used `wwwroot/logos/bookstore-logo.png` path. Then copy the Brand component's view ([from here](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/Default.cshtml)) from the basic theme files under the `Themes/Basic/Components/Brand` folder. The result should be similar the picture below:
@ -154,7 +154,7 @@ Now, you can run the application to see the result:
If you need, you can also replace [the code behind c# class](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/MainNavbarBrandViewComponent.cs) of the component just using the dependency injection system.
## Overriding the Theme
### Overriding the Theme
Just as explained above, you can replace any component, layout or c# class of the used theme. See the [theming document](Theming.md) for more information on the theming system.
@ -164,11 +164,11 @@ Overriding a static embedded resource (like JavaScript, Css or image files) of a
## Manipulating the Bundles
The [Bundling & Minification](Bundling-Minification.md) system provides an extensible and dynamic system to create script and style bundles. It allows to extend and manipulate existing bundles by design.
The [Bundling & Minification](Bundling-Minification.md) system provides an **extensible and dynamic** system to create **script** and **style** bundles. It allows you to extend and manipulate the existing bundles.
### Example: Add a Global CSS File
For example, ABP Framework defines a global CSS bundle which is added to every page (actually, added to the layout file by the themes). Let's add a custom CSS file to the end of the bundle files, so we can override any global style.
For example, ABP Framework defines a **global style bundle** which is added to every page (actually, added to the layout by the themes). Let's add a **custom style file** to the end of the bundle files, so we can override any global style.
First, create a CSS file and locate it in a folder inside the `wwwroot`:
@ -209,7 +209,7 @@ Just like the `StandardBundles.Styles.Global`, there is a `StandardBundles.Scrip
### Example: Manipulate the Bundle Files
The example above adds a new file to the bundle. You can do more if you create a bundle contributor class. Example:
The example above adds a new file to the bundle. You can do more if you create a **bundle contributor** class. Example:
````csharp
public class MyGlobalStyleBundleContributor : BundleContributor
@ -241,9 +241,9 @@ It is not a good idea to clear all CSS files. In a real world scenario, you can
### Example: Add a JavaScript File for a Specific Page
The examples above works with the global bundle added to the layout. What if you want to add a CSS/JavaScript file (or replace a file) for a specific page defines inside a depended module.
The examples above works with the global bundle added to the layout. What if you want to add a CSS/JavaScript file (or replace a file) for a specific page defines inside a depended module?
Assume that you want to run a JavaScript code once user enters to the Role Management page of the Identity Module.
Assume that you want to run a **JavaScript code** once the user enters to the **Role Management** page of the Identity Module.
First, create a standard JavaScript file under the `wwwroot`, `Pages` or `Views` folder (ABP support to add static resources inside these folders by default). We prefer the `Pages/Identity/Roles` folder to follow the conventions:
@ -276,35 +276,195 @@ Configure<AbpBundlingOptions>(options =>
> Notice that not every page defines such page bundles. They define only if needed.
In addition to adding new CSS/JavaScript file to a page, you know you also can replace the existing one.
In addition to adding new CSS/JavaScript file to a page, you also can replace the existing one (by defining a bundle contributor).
## Layout Customization
Layouts are defined by the theme ([see the theming](Theming.md)) by design. They are not included in a downloaded application solution. In this way you can easily upgrade the theme and get new features.
Layouts are defined by the theme ([see the theming](Theming.md)) by design. They are not included in a downloaded application solution. In this way you can easily **upgrade** the theme and get new features. You can not **directly change** the layout code in your application unless you replace it by your own layout (will be explained in the next sections).
However, there are some common ways to customize the layout described in the next sections.
There are some common ways to **customize the layout** described in the next sections.
### Menu Contributors
There are two standard menus defined by the ABP Framework:
There are two **standard menus** defined by the ABP Framework:
![bookstore-menus-highlighted](../../images/bookstore-menus-highlighted.png)
* `StandardMenus.Main`: The main menu of the application.
* `StandardMenus.User`: The user menu (generally at the top right of the screen).
Rendering the menus is a responsibility of the theme, but menu items are determined by the modules and your application code. Just implement the `IMenuContributor` interface and manipulate the menu items in the `ConfigureMenuAsync` method.
Rendering the menus is a responsibility of the theme, but **menu items** are determined by the modules and your application code. Just implement the `IMenuContributor` interface and **manipulate the menu items** in the `ConfigureMenuAsync` method.
Menu contributors are executed whenever need to render the menu. There is already a menu contributor defined in the application startup template, so you can take it as an example and improve if necessary. See the [navigation menu](Navigation-Menu.md) document for more.
Menu contributors are executed whenever need to render the menu. There is already a menu contributor defined in the **application startup template**, so you can take it as an example and improve if necessary. See the [navigation menu](Navigation-Menu.md) document for more.
### Toolbar Contributors
TODO
[Toolbar system](Toolbars.md) is used to define **toolbars** on the user interface. Modules (or your application) can add **items** to a toolbar, then the theme renders the toolbar on the **layout**.
### Layouts
There is only one **standard toolbar** (named "Main" - defined as a constant: `StandardToolbars.Main`). For the basic theme, it is rendered as shown below:![bookstore-toolbar-highlighted](../../images/bookstore-toolbar-highlighted.png)
In the screenshot above, there are two items added to the main toolbar: Language switch component & user menu. You can add your own items here.
#### Example: Add a Notification Icon
In this example, we will add a **notification (bell) icon** to the left of the language switch item. A item in the toolbar should be a **view component**. So, first, create a new view component in your project:
![bookstore-notification-view-component](../../images/bookstore-notification-view-component.png)
**NotificationViewComponent.cs**
````csharp
public class NotificationViewComponent : AbpViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
return View("/Pages/Shared/Components/Notification/Default.cshtml");
}
}
````
**Default.cshtml**
````xml
<div id="MainNotificationIcon" style="color: white; margin: 8px;">
<i class="far fa-bell"></i>
</div>
````
TODO
Now, we can create a class implementing the `IToolbarContributor` interface:
````csharp
public class MyToolbarContributor : IToolbarContributor
{
public Task ConfigureToolbarAsync(IToolbarConfigurationContext context)
{
if (context.Toolbar.Name == StandardToolbars.Main)
{
context.Toolbar.Items
.Insert(0, new ToolbarItem(typeof(NotificationViewComponent)));
}
return Task.CompletedTask;
}
}
````
This class adds the `NotificationViewComponent` as the first item in the `Main` toolbar.
Finally, you need to add this contributor to the `AbpToolbarOptions`, in the `ConfigureServices` of your module:
````csharp
Configure<AbpToolbarOptions>(options =>
{
options.Contributors.Add(new MyToolbarContributor());
});
````
That's all, you will see the notification icon on the toolbar when you run the application:
![bookstore-notification-icon-on-toolbar](../../images/bookstore-notification-icon-on-toolbar.png)
`NotificationViewComponent` in this sample simply returns a view without any data. In real life, you probably want to **query database** (or call an HTTP API) to get notifications and pass to the view. If you need, you can add a `JavaScript` or `CSS` file to the global bundle (as described before) for your toolbar item.
See the [toolbars document](Toolbars.md) for more about the toolbar system.
### Layout Hooks
TODO
[Layout Hooks](Layout-Hooks.md) system allows you to **add code** at some specific parts of the layout. All layouts of all themes should implement these hooks. Then you can then 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. You (or the modules you are using) can add multiple items to the same hook. All of them will be added to 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.
### 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 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.
#### ITheme
ABP Framework uses the `ITheme` service to get the layout location by the layout name. You can replace this service to dynamically select the layout location.
#### IThemeManager
`IThemeManager` is used to obtain the current theme and get the layout path. Any page can determine the layout of its own. Example:
````html
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@inject IThemeManager ThemeManager
@{
Layout = ThemeManager.CurrentTheme.GetLayout(StandardLayouts.Empty);
}
````
This page will use the empty layout. You use `ThemeManager.CurrentTheme.GetEmptyLayout();` extension method as a shortcut.
If you want to set the layout for all the pages under a specific folder, then write the code above in a `_ViewStart.cshtml` file under that folder.

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

@ -0,0 +1,3 @@
# Layout Hooks
TODO

3
docs/en/UI/AspNetCore/Libraries/DatatablesNet.md

@ -0,0 +1,3 @@
# ABP Datatables.Net Integration for ASP.NET Core UI
TODO

82
docs/en/UI/AspNetCore/Tag-Helpers/Alerts.md

@ -0,0 +1,82 @@
# Alerts
## Introduction
`abp-alert` is a main element to create an alert.
Basic usage:
````xml
<abp-alert alert-type="Primary">
A simple primary alert—check it out!
</abp-alert>
````
## Demo
See the [alerts demo page](https://bootstrap-taghelpers.abp.io/Components/Alerts) to see it in action.
## Attributes
### alert-type
A value indicates the type of the alert. Should be one of the following values:
* `Default` (default value)
* `Primary`
* `Secondary`
* `Success`
* `Danger`
* `Warning`
* `Info`
* `Light`
* `Dark`
Example:
````xml
<abp-alert alert-type="Warning">
A simple warning alert—check it out!
</abp-alert>
````
### alert-link
A value provides matching colored links within any alert.
Example:
````xml
<abp-alert alert-type="Danger">
A simple danger alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
````
### dismissible
A value to make the alert dismissible.
Example:
````xml
<abp-alert alert-type="Warning" dismissible="true">
Holy guacamole! You should check in on some of those fields below.
</abp-alert>
````
### Additional content
`abp-alert` can also contain additional HTML elements like headings, paragraphs and dividers.
Example:
````xml
<abp-alert alert-type="Success">
<h4>Well done!</h4>
<p>Aww yeah, you successfully read this important alert message. This example text is going to run a bit longer so that you can see how spacing within an alert works with this kind of content.</p>
<hr>
<p class="mb-0">Whenever you need to, be sure to use margin utilities to keep things nice and tidy.</p>
</abp-alert>
````

188
docs/en/UI/AspNetCore/Tag-Helpers/Cards.md

@ -0,0 +1,188 @@
# Cards
## Introduction
`abp-card` is a content container derived from bootstrap card element.
Basic usage:
````xml
<abp-card style="width: 18rem;">
<img abp-card-image="Top" src="~/imgs/demo/300x200.png"/>
<abp-card-body>
<abp-card-title>Card Title</abp-card-title>
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text>
<a abp-button="Primary" href="#"> Go somewhere</a>
</abp-card-body>
</abp-card>
````
##### Using Titles, Text and Links:
Following tags can be used under main `abp-card` tag
* `abp-card-title`
* `abp-card-subtitle`
* `a abp-card-link`
Sample:
````xml
<abp-card style="width: 18rem;">
<abp-card-body>
<abp-card-title>Card title</abp-card-title>
<abp-card-subtitle class="mb-2 text-muted">Card subtitle</abp-card-subtitle>
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text>
<a abp-card-link href="#">Card link</a>
<a abp-card-link href="#">Another link</a>
</abp-card-body>
</abp-card>
````
##### Using List Groups:
* `abp-list-group flush="true"` : `flush` attribute renders into bootstrap `list-group-flush` class which is used for removing borders and rounded corners to render list group items edge to edge in a parent container.
* `abp-list-group-item`
Kitchen Sink Sample:
````xml
<abp-card style="width: 18rem;">
<img abp-card-image="Top" src="~/imgs/demo/300x200.png" />
<abp-card-body>
<abp-card-title>Card Title</abp-card-title>
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text>
</abp-card-body>
<abp-list-group flush="true">
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item>Vestibulum at eros</abp-list-group-item>
</abp-list-group>
<abp-card-body>
<a abp-card-link href="#">Card link</a>
<a abp-card-link href="#">Another link</a>
</abp-card-body>
</abp-card>
````
##### Using Header, Footer and Blockquote:
* `abp-card-header`
* `abp-card-footer`
* `abp-blockquote`
Sample:
```xml
<abp-card style="width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
<a abp-button="Primary" href="#"> Go somewhere</a>
</abp-card-body>
</abp-card>
```
Quote Sample:
```xml
<abp-card>
<abp-card-header>Quote</abp-card-header>
<abp-card-body>
<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>
</abp-card-body>
</abp-card>
```
Footer Sample:
```xml
<abp-card class="text-center">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<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>
</abp-card-body>
<abp-card-footer class="text-muted"> 2 days ago</abp-card-footer>
</abp-card>
```
## Demo
See the [cards demo page](https://bootstrap-taghelpers.abp.io/Components/Cards) to see it in action.
## abp-card Attributes
- **background:** A value indicates the background color of the card.
- **text-color**: A value indicates the color of the text inside the card.
- **border:** A value indicates the color of the border inside the card.
Should be one of the following values:
* `Default` (default value)
* `Primary`
* `Secondary`
* `Success`
* `Danger`
* `Warning`
* `Info`
* `Light`
* `Dark`
Example:
````xml
<abp-card background="Success" text-color="Danger" border="Dark">
````
### sizing
Cards has default 100% with and can be changed with custom CSS, grid classes, grid Sass mixins or [utilities](https://getbootstrap.com/docs/4.0/utilities/sizing/).
````xml
<abp-card style="width: 18rem;">
````
### card-deck and card-columns
`abp-card` can be used inside `card-deck` or `card-columns` aswell.
````xml
<div class="card-deck">
<abp-card background="Primary">
<abp-card-header>First Deck</abp-card-header>
<abp-card-body>
<abp-card-title> Ace </abp-card-title>
<abp-card-text>Here is the content for Ace.</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card background="Info">
<abp-card-header>Second Deck</abp-card-header>
<abp-card-body>
<abp-card-title> Beta </abp-card-title>
<abp-card-text>Beta content.</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card background="Warning">
<abp-card-header>Third Deck</abp-card-header>
<abp-card-body>
<abp-card-title> Epsilon </abp-card-title>
<abp-card-text>Content for Epsilon.</abp-card-text>
</abp-card-body>
</abp-card>
</div>
````

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

@ -0,0 +1,92 @@
# Collapse
## Introduction
`abp-collapse-body` is the main container for showing and hiding content. `abp-collapse-id` is used to show and hide the content container. Can be triggered with both `abp-button` and `a` tags.
Basic usage:
````xml
<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>
<abp-collapse-body id="collapseExample">
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-collapse-body>
````
## Demo
See the [collapse demo page](https://bootstrap-taghelpers.abp.io/Components/Collapse) to see it in action.
## Attributes
### show
A value indicates if the collapse body will be initialized visible or hidden. Should be one of the following values:
* `false` (default value)
* `true`
### multi
A value indicates if an `abp-collapse-body` can be shown or hidden by an element that can show/hide multiple collapse bodies. Basically, this attribute adds "multi-collapse" class to `abp-collapse-body`. Should be one of the following values:
* `false` (default value)
* `true`
Sample:
````xml
<a abp-button="Primary" abp-collapse-id="FirstCollapseExample"> Toggle first element </a>
<abp-button button-type="Primary" abp-collapse-id="SecondCollapseExample" text="Toggle second element" />
<abp-button button-type="Primary" abp-collapse-id="FirstCollapseExample SecondCollapseExample" text="Toggle both elements" />
<abp-row class="mt-3">
<abp-column size-sm="_6">
<abp-collapse-body id="FirstCollapseExample" multi="true">
Curabitur porta porttitor libero eu luctus. Praesent ultrices mattis commodo. Integer sodales massa risus, in molestie enim sagittis blandit
</abp-collapse-body>
</abp-column>
<abp-column size-sm="_6">
<abp-collapse-body id="SecondCollapseExample" multi="true">
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et.
</abp-collapse-body>
</abp-column>
</abp-row>
````
## Accordion example
`abp-accordion` is the main container for the accordion items.
Basic usage:
````xml
<abp-accordion>
<abp-accordion-item title="Collapsible Group Item #1">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry rtat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
<abp-accordion-item title="Collapsible Group Item #2">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
<abp-accordion-item title="Collapsible Group Item #3">
Anim pariatur wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
</abp-accordion>
````
## Attributes
### active
A value indicates if the accordion item will be initialized visible or hidden. Should be one of the following values:
* `false` (default value)
* `true`
### title
A value indicates the visible title of the accordion item. Should be a string value.

97
docs/en/UI/AspNetCore/Tag-Helpers/Dropdowns.md

@ -0,0 +1,97 @@
# Dropdowns
## Introduction
`abp-dropdown` is the main container for dropdown content.
Basic usage:
````xml
<abp-dropdown>
<abp-dropdown-button text="Dropdown button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
````
## Demo
See the [dropdown demo page](https://bootstrap-taghelpers.abp.io/Components/Dropdowns) to see it in action.
## Attributes
### direction
A value indicates which direction the dropdown buttons will be displayed to. Should be one of the following values:
* `Down` (default value)
* `Up`
* `Right`
* `Left`
### dropdown-style
A value indicates if an `abp-dropdown-button` will have split icon for dropdown. Should be one of the following values:
* `Single` (default value)
* `Split`
## Menu items
`abp-dropdown-menu` is the main container for dropdown menu items.
Basic usage:
````xml
<abp-dropdown>
<abp-dropdown-button button-type="Secondary" text="Dropdown"/>
<abp-dropdown-menu>
<abp-dropdown-header>Dropdown Header</abp-dropdown-header>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item active="true" href="#">Active action</abp-dropdown-item>
<abp-dropdown-item disabled="true" href="#">Disabled action</abp-dropdown-item>
<abp-dropdown-divider/>
<abp-dropdown-item-text>Dropdown Item Text</abp-dropdown-item-text>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
````
## Attributes
### align
A value indicates which direction `abp-dropdown-menu` items will be aligned to. Should be one of the following values:
* `Left` (default value)
* `Right`
### Additional content
`abp-dropdown-menu` can also contain additional HTML elements like headings, paragraphs, dividers or form element.
Example:
````xml
<abp-dropdown >
<abp-dropdown-button button-type="Secondary" text="Dropdown With Form"/>
<abp-dropdown-menu>
<form class="px-4 py-3">
<abp-input asp-for="EmailAddress"></abp-input>
<abp-input asp-for="Password"></abp-input>
<abp-input asp-for="RememberMe"></abp-input>
<abp-button button-type="Primary" text="Sign In" type="submit" />
</form>
<abp-dropdown-divider></abp-dropdown-divider>
<abp-dropdown-item href="#">New around here? Sign up</abp-dropdown-item>
<abp-dropdown-item href="#">Forgot password?</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
````

286
docs/en/UI/AspNetCore/Tag-Helpers/Grids.md

@ -0,0 +1,286 @@
# Grids
## Introduction
Abp tag helpers for bootstrap based grid system.
## Demo
See the [grids demo page](https://bootstrap-taghelpers.abp.io/Components/Grids) to see it in action.
### Sizing
**Equal Width:** Creates columns with equal width.
Sample:
````xml
<abp-container>
<abp-row>
<abp-column abp-border="Info">1 of 2</abp-column>
<abp-column abp-border="Danger">2 of 2</abp-column>
</abp-row>
<abp-row>
<abp-column abp-border="Primary">1 of 3</abp-column>
<abp-column abp-border="Secondary">2 of 3</abp-column>
<abp-column abp-border="Dark">3 of 3</abp-column>
</abp-row>
</abp-container>
````
**Column Breaker:** `abp-column-breaker` is used for breaking the automatic width of placement of the current row and starting in a new row afterwards.
Sample:
````xml
<abp-container>
<abp-row>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
<abp-column-breaker/>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
</abp-row>
</abp-container>
````
**Setting one column width:** size attribute is used for setting the width for a specific column.
Sample:
```xml
<abp-container>
<abp-row>
<abp-column>1 of 3</abp-column>
<abp-column size="_6">2 of 3 (wider)</abp-column>
<abp-column>3 of 3</abp-column>
</abp-row>
<abp-row>
<abp-column>1 of 3</abp-column>
<abp-column size="_5">2 of 3 (wider)</abp-column>
<abp-column>3 of 3</abp-column>
</abp-row>
</abp-container>
```
**Variable width content:** Auto resizing column based on content.
```xml
<abp-container>
<abp-row h-align="Center">
<abp-column size-lg="_2" abp-border="Info">1 of 3</abp-column>
<abp-column size-md="Auto" abp-border="Danger">Contrary to popular belief, Lorem Ipsum is not simply random text.</abp-column>
<abp-column size-lg="_2" abp-border="Warning">3 of 3</abp-column>
</abp-row>
<abp-row>
<abp-column>1 of 3</abp-column>
<abp-column size-md="Auto">Variable width content</abp-column>
<abp-column size-lg="_2">3 of 3</abp-column>
</abp-row>
</abp-container>
```
### Responsive Classes
Responsive classes can be used strongly typed within abp tags.
```xml
<abp-row>
<abp-column size-sm="_8">col-sm-8</abp-column>
<abp-column size-sm="_4">col-sm-4</abp-column>
</abp-row>
<abp-row>
<abp-column size-sm="_">col-sm</abp-column>
<abp-column size-sm="_">col-sm</abp-column>
<abp-column size-sm="_">col-sm</abp-column>
<abp-column size-sm="_">col-sm</abp-column>
</abp-row>
<!-- Stack the columns on mobile by making one full-width and the other half-width -->
<abp-row>
<abp-column size="_12" size-md="_8">.col-12 .col-md-8</abp-column>
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column>
</abp-row>
<!-- Columns start at 50% wide on mobile and bump up to 33.3% wide on desktop -->
<abp-row>
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column>
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column>
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column>
</abp-row>
<!-- Columns are always 50% wide, on mobile and desktop -->
<abp-row>
<abp-column size="_6">.col-6</abp-column>
<abp-column size="_6">.col-6</abp-column>
</abp-row>
```
### Alignment
Column alignments can be done strongly typed in abp tags with both vertically and horizontally.
**Vertical-alignment**: `v-align` attribute value is used to align the columns vertically.
Sample:
```xml
<abp-container>
<abp-row v-align="Start">
<abp-column>column</abp-column>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
</abp-row>
<abp-row v-align="Center">
<abp-column>column</abp-column>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
</abp-row>
<abp-row v-align="End">
<abp-column>column</abp-column>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
</abp-row>
</abp-container>
```
**Horizontal-alignment**: `h-align` attribute value is used to align the columns horizontally.
Sample:
```xml
<abp-container>
<abp-row h-align="Start">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
<abp-row h-align="Center">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
<abp-row h-align="End">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
<abp-row h-align="Around">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
<abp-row h-align="Between">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
</abp-container>
```
**No gutters**: The gutters between columns in predefined grid classes can be removed with `gutters="false"`. This removes the negative `margin`s from `abp-row` and the horizontal `padding` from all immediate children columns.
Sample:
```xml
<abp-row gutters="false">
<abp-column size="_8">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
```
**Column wrapping**: If more than 12 columns are placed within a single row, each group of extra columns will, as one unit, wrap onto a new line.
Sample:
```xml
<abp-row>
<abp-column size="_9">.col-9</abp-column>
<abp-column size="_4">.col-4<br>Since 9 + 4 = 13 &gt; 12, this 4-column-wide div gets wrapped onto a new line as one contiguous unit.</abp-column>
<abp-column size="_6">.col-6<br>Subsequent columns continue along the new line.s</abp-column>
</abp-row>
```
### Reordering
**Order Classes**: `order` attribute is used for controlling the visual order of the content.
Sample:
```xml
<abp-container>
<abp-row>
<abp-column order="_12">First, but Last</abp-column>
<abp-column>Second, but unordered</abp-column>
<abp-column order="_6">Third, but Second</abp-column>
</abp-row>
</abp-container>
```
**Offsetting columns**: `offset` attribute is used for setting the offset of the grid columns.
Sample:
```xml
<abp-container>
<abp-row>
<abp-column size-md="_4">.col-md-4</abp-column>
<abp-column size-md="_4" offset-md="_4">.col-md-4 .offset-md-4</abp-column>
</abp-row>
<abp-row>
<abp-column size-md="_3" offset-md="_3">.col-md-3 .offset-md-3</abp-column>
<abp-column size-md="_3" offset-md="_3">.col-md-3 .offset-md-3</abp-column>
</abp-row>
<abp-row>
<abp-column size-md="_6" offset-md="_3">.col-md-6 .offset-md-3</abp-column>
</abp-row>
<abp-row>
<abp-column size-sm="_5" size-md="_6">.col-sm-5 .col-md-6</abp-column>
<abp-column size-sm="_5" offset-sm="_2" size-md="_6" offset-md="_">.col-sm-5 .offset-sm-2 .col-md-6 .offset-md-0</abp-column>
</abp-row>
<abp-row>
<abp-column size-sm="_6" size-md="_5" size-lg="_6">col-sm-6 .col-md-5 .col-lg-6</abp-column>
<abp-column size-sm="_6" size-md="_5" offset-md="_2" size-lg="_6" offset-lg="_">.col-sm-6 .col-md-5 .offset-md-2 .col-lg-6 .offset-lg-0</abp-column>
</abp-row>
</abp-container>
```
## abp-row Attributes
- **v-align:** A value indicates the vertical positioning of the containing columns. Should be one of the following values:
* `Default` (default value)
* `Start`
* `Center`
* `End`
- **h-align**: A value indicates the horizontal positioning of the containing columns. Should be one of the following values:
* `Default` (default value)
* `Start`
* `Center`
* `Around`
* `Between`
* `End`
- **gutter**: A value indicates if the negative `margin` and horizontal `padding` will be removed from all children columns. Will act as `true` value if this attribute is not set. Should be one of the following values:
* `true`
* `false`
## abp-column Attributes
- **size:** A value indicates the width of the column from `_`, `Undefined`, `_1`..`_12`, `Auto`. Or can be used with predefined values like:
- `size-sm`
- `size-md`
- `size-lg`
- `size-xl`
- **order**: A value indicates the order of column from `Undefined`, `_1`..`_12`, `First` and `Last`.
- **offset:** A value indicates offset of the column from `_`, `Undefined`, `_1`..`_12`, `Auto`. Or can be used with predefined values like:
- `offset-sm`
- `offset-md`
- `offset-lg`
- `offset-xl`

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

@ -13,6 +13,18 @@ ABP Framework also adds some **useful features** to the standard bootstrap compo
Here, the list of components those are wrapped by the ABP Framework:
* [Buttons](Buttons.md)
* [Cards](Cards.md)
* [Alerts](Alerts.md)
* [Tabs](Tabs.md)
* [Grids](Grids.md)
* [Modals](Modals.md)
* [Collapse](Collapse.md)
* [Dropdowns](Dropdowns.md)
* [List Groups](List-Groups.md)
* [Paginator](Paginator.md)
* [Popovers](Popovers.md)
* [Progress Bars](Progress-Bars.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.

78
docs/en/UI/AspNetCore/Tag-Helpers/List-Groups.md

@ -0,0 +1,78 @@
# List Groups
## Introduction
`abp-list-group` is the main container for list group content.
Basic usage:
````xml
<abp-list-group>
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item>Morbi leo risus</abp-list-group-item>
<abp-list-group-item>Vestibulum at eros</abp-list-group-item>
</abp-list-group>
````
## Demo
See the [list groups demo page](https://bootstrap-taghelpers.abp.io/Components/ListGroups) to see it in action.
## Attributes
### flush
A value indicates `abp-list-group` items to remove some borders and rounded corners to render list group items edge-to-edge in a parent container. Should be one of the following values:
* `false` (default value)
* `true`
### active
A value indicates if an `abp-list-group-item` to be active. Should be one of the following values:
* `false` (default value)
* `true`
### disabled
A value indicates if an `abp-list-group-item` to be disabled. Should be one of the following values:
* `false` (default value)
* `true`
### href
A value indicates if an `abp-list-group-item` has a link. Should be a string link value.
### type
A value indicates an `abp-list-group-item` style class with a stateful background and color. Should be one of the following values:
* `Default` (default value)
* `Primary`
* `Secondary`
* `Success`
* `Danger`
* `Warning`
* `Info`
* `Light`
* `Dark`
* `Link`
### Additional content
`abp-list-group-item` can also contain additional HTML elements like spans.
Example:
````xml
<abp-list-group>
<abp-list-group-item>Cras justo odio <span abp-badge-pill="Primary">14</span></abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in <span abp-badge-pill="Primary">2</span></abp-list-group-item>
<abp-list-group-item>Morbi leo risus <span abp-badge-pill="Primary">1</span></abp-list-group-item>
</abp-list-group>
````

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

@ -0,0 +1,81 @@
# Modals
## Introduction
`abp-modal` is a main element to create a modal.
Basic usage:
````xml
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button>
<abp-modal centered="true" size="Large" id="myModal">
<abp-modal-header title="Modal title"></abp-modal-header>
<abp-modal-body>
Woohoo, you're reading this text in a modal!
</abp-modal-body>
<abp-modal-footer buttons="Close"></abp-modal-footer>
</abp-modal>
````
## Demo
See the [modals demo page](https://bootstrap-taghelpers.abp.io/Components/Modals) to see it in action.
## Attributes
### centered
A value indicates the positioning of the modal. Should be one of the following values:
* `false` (default value)
* `true`
### size
A value indicates the size of the modal. Should be one of the following values:
* `Default` (default value)
* `Small`
* `Large`
* `ExtraLarge`
### static
A value indicates if the modal will be static. Should be one of the following values:
* `false` (default value)
* `true`
### Additional content
`abp-modal-footer` can have multiple buttons with alignment option.
Add `@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal` to your page.
Example:
````xml
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button>
<abp-modal centered="true" size="Large" id="myModal" static="true">
<abp-modal-header title="Modal title"></abp-modal-header>
<abp-modal-body>
Woohoo, you're reading this text in a modal!
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Save|AbpModalButtons.Close)" button-alignment="Between"></abp-modal-footer>
</abp-modal>
````
### button-alignment
A value indicates the positioning of your modal footer buttons. Should be one of the following values:
* `Default` (default value)
* `Start`
* `Center`
* `Around`
* `Between`
* `End`

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

@ -0,0 +1,57 @@
# Paginator
## Introduction
`abp-paginator` is the abp tag for pagination. Requires `Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` type of model.
Basic usage:
````xml
<abp-paginator model="Model.PagerModel" show-info="true"></abp-paginator>
````
Model:
````xml
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
{
public class PaginatorModel : PageModel
{
public PagerModel PagerModel { get; set; }
public void OnGet(int currentPage, string sort)
{
PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort);
}
}
}
````
## Demo
See the [paginator demo page](https://bootstrap-taghelpers.abp.io/Components/Paginator) to see it in action.
## Attributes
### model
`Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` type of model can be initialized with the following data:
* `totalCount`
* `shownItemsCount`
* `currentPage`
* `pageSize`
* `pageUrl`
* `sort` (default null)
### show-info
A value indicates if an extra information about start, end and total records will be displayed. Should be one of the following values:
* `false` (default value)
* `true`

70
docs/en/UI/AspNetCore/Tag-Helpers/Progress-Bars.md

@ -0,0 +1,70 @@
# Progress Bars
## Introduction
`abp-progress-bar` is the abp tag for progress bar status.
Basic usage:
````xml
<abp-progress-bar value="70" />
<abp-progress-bar type="Warning" value="25"> %25 </abp-progress-bar>
<abp-progress-bar type="Success" value="40" strip="true"/>
<abp-progress-bar type="Dark" value="10" min-value="5" max-value="15" strip="true"> %50 </abp-progress-bar>
<abp-progress-group>
<abp-progress-part type="Success" value="25"/>
<abp-progress-part type="Danger" value="10" strip="true"> %10 </abp-progress-part>
<abp-progress-part type="Primary" value="50" animation="true" strip="true" />
</abp-progress-group>
````
## Demo
See the [progress bars demo page](https://bootstrap-taghelpers.abp.io/Components/Progress-Bars) to see it in action.
## Attributes
### value
A value indicates the current progress of the bar.
### type
A value indicates the background color of the progress bar. Should be one of the following values:
* `Default` (default value)
* `Secondary`
* `Success`
* `Danger`
* `Warning`
* `Info`
* `Light`
* `Dark`
### min-value
Minimum value of the progress bar. Default is 0.
### max-value
Maximum value of the progress bar. Default is 100.
### strip
A value indicates if the background style of the progress bar is stripped. Should be one of the following values:
* `false` (default value)
* `true`
### animation
A value indicates if the stripped background style of the progress bar is animated. Should be one of the following values:
* `false` (default value)
* `true`

93
docs/en/UI/AspNetCore/Tag-Helpers/Tabs.md

@ -0,0 +1,93 @@
# Tabs
## Introduction
`abp-tab` is the basic tab navigation content container derived from bootstrap tab element.
Basic usage:
````xml
<abp-tabs>
<abp-tab title="Home">
Content_Home
</abp-tab>
<abp-tab-link title="Link" href="#" />
<abp-tab title="profile">
Content_Profile
</abp-tab>
<abp-tab-dropdown title="Contact" name="ContactDropdown">
<abp-tab title="Contact 1" parent-dropdown-name="ContactDropdown">
Content_1_Content
</abp-tab>
<abp-tab title="Contact 2" parent-dropdown-name="ContactDropdown">
Content_2_Content
</abp-tab>
</abp-tab-dropdown>
</abp-tabs>
````
## Demo
See the [cards demo page](https://bootstrap-taghelpers.abp.io/Components/Cards) to see it in action.
## abp-tab Attributes
- **title**: Sets the text of the tab menu.
- **name:** Sets "id" attribute of generated elements. Default value is a Guid. Not needed unless tabs are changed or modified with Jquery.
- **active**: Sets the active tab.
Example:
````xml
<abp-tabs name="TabId">
<abp-tab name="nav-home" title="Home">
Content_Home
</abp-tab>
<abp-tab name="nav-profile" active="true" title="profile">
Content_Profile
</abp-tab>
<abp-tab name="nav-contact" title="Contact">
Content_Contact
</abp-tab>
</abp-tabs>
````
### Pills
Example:
````xml
<abp-tabs tab-style="Pill">
<abp-tab title="Home">
Content_Home
</abp-tab>
<abp-tab title="profile">
Content_Profile
</abp-tab>
<abp-tab title="Contact">
Content_Contact
</abp-tab>
</abp-tabs>
````
### Vertical
**vertical-header-size**: Sets the column width of tab headers.
Example:
````xml
<abp-tabs tab-style="PillVertical" vertical-header-size="_2" >
<abp-tab active="true" title="Home">
Content_Home
</abp-tab>
<abp-tab title="profile">
Content_Profile
</abp-tab>
<abp-tab title="Contact">
Content_Contact
</abp-tab>
</abp-tabs>
````

35
docs/en/UI/AspNetCore/Tag-Helpers/Tooltips.md

@ -0,0 +1,35 @@
# Tooltips
## Introduction
`abp-tooltip` is the abp tag for tooltips.
Basic usage:
````xml
<abp-button abp-tooltip="Tooltip">
Tooltip Default
</abp-button>
<abp-button abp-tooltip-top="Tooltip">
Tooltip on top
</abp-button>
<abp-button abp-tooltip-right="Tooltip">
Tooltip on right
</abp-button>
<abp-button abp-tooltip-bottom="Tooltip">
Tooltip on bottom
</abp-button>
<abp-button disabled="true" abp-tooltip="Tooltip">
Disabled button Tooltip
</abp-button>
````
## Demo
See the [tooltips demo page](https://bootstrap-taghelpers.abp.io/Components/Tooltips) to see it in action.

3
docs/en/UI/AspNetCore/Toolbars.md

@ -0,0 +1,3 @@
# Toolbars
TODO

14
docs/en/UI/AspNetCore/Widgets.md

@ -4,8 +4,8 @@ ABP provides a model and infrastructure to create **reusable widgets**. Widget s
* Have **scripts & styles** dependencies for your widget.
* Create **dashboards** with widgets used inside.
* Define widgets in reusable **[modules](../Module-Development-Basics.md)**.
* Co-operate widgets with **[authorization](../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems.
* Define widgets in reusable **[modules](../../Module-Development-Basics.md)**.
* Co-operate widgets with **[authorization](../../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems.
## Basic Widget Definition
@ -13,7 +13,7 @@ ABP provides a model and infrastructure to create **reusable widgets**. Widget s
As the first step, create a new regular ASP.NET Core View Component:
![widget-basic-files](../images/widget-basic-files.png)
![widget-basic-files](../../images/widget-basic-files.png)
**MySimpleWidgetViewComponent.cs**:
@ -184,7 +184,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
See [the localization document](../Localization.md) to learn about localization resources and keys.
See [the localization document](../../Localization.md) to learn about localization resources and keys.
## Style & Script Dependencies
@ -220,7 +220,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
ABP takes account these dependencies and properly adds to the view/page when you use the widget. Style/script files can be **physical or virtual**. It is completely integrated to the [Virtual File System](../Virtual-File-System.md).
ABP takes account these dependencies and properly adds to the view/page when you use the widget. Style/script files can be **physical or virtual**. It is completely integrated to the [Virtual File System](../../Virtual-File-System.md).
### Defining Bundle Contributors
@ -454,7 +454,7 @@ Used to refresh the widget when needed. It has a filter argument that can be use
Some widgets may need to be available only for authenticated or authorized users. In this case, use the following properties of the `Widget` attribute:
* `RequiresAuthentication` (`bool`): Set to true to make this widget usable only for authentication users (user have logged in to the application).
* `RequiredPolicies` (`List<string>`): A list of policy names to authorize the user. See [the authorization document](../Authorization.md) for more info about policies.
* `RequiredPolicies` (`List<string>`): A list of policy names to authorize the user. See [the authorization document](../../Authorization.md) for more info about policies.
Example:
@ -487,7 +487,7 @@ Configure<AbpWidgetOptions>(options =>
});
```
Write this into the `ConfigureServices` method of your [module](../Module-Development-Basics.md). All the configuration done with the `Widget` attribute is also possible with the `AbpWidgetOptions`. Example configuration that adds a style for the widget:
Write this into the `ConfigureServices` method of your [module](../../Module-Development-Basics.md). All the configuration done with the `Widget` attribute is also possible with the `AbpWidgetOptions`. Example configuration that adds a style for the widget:
````csharp
Configure<AbpWidgetOptions>(options =>

16
docs/en/docs-nav.json

@ -302,12 +302,20 @@
{
"text": "Theming",
"path": "UI/AspNetCore/Theming.md"
},
{
"text": "Customize/Extend the UI",
"path": "UI/AspNetCore/Customization-User-Interface.md"
}
]
},
{
"text": "Angular",
"items": [
{
"text": "HTTP Requests",
"path": "UI/Angular/HTTP-Requests.md"
},
{
"text": "Localization",
"path": "UI/Angular/Localization.md"
@ -316,6 +324,10 @@
"text": "Permission Management",
"path": "UI/Angular/Permission-Management.md"
},
{
"text": "Config State",
"path": "UI/Angular/Config-State.md"
},
{
"text": "Component Replacement",
"path": "UI/Angular/Component-Replacement.md"
@ -323,6 +335,10 @@
{
"text": "Custom Setting Page",
"path": "UI/Angular/Custom-Setting-Page.md"
},
{
"text": "TrackByService",
"path": "UI/Angular/Track-By-Service.md"
}
]
}

BIN
docs/en/images/bookstore-google-analytics-view-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/images/bookstore-notification-icon-on-toolbar.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
docs/en/images/bookstore-notification-view-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/images/bookstore-toolbar-highlighted.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
docs/en/images/docs-module_download-new-abp-project.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 66 KiB

BIN
docs/en/images/docs-module_solution-explorer.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 21 KiB

55
docs/zh-Hans/Application-Services.md

@ -200,7 +200,7 @@ public async Task<BookDto> GetAsync(Guid id)
### CRUD应用服务
如果需要创建具有Create,Update,Delete和Get方法的简单CRUD应用服务,则可以使用ABP的基类轻松构建服务. 你可以继承CrudAppService.
如果需要创建具有Create,Update,Delete和Get方法的简单**CRUD应用服务**,则可以使用ABP的基类轻松构建服务. 你可以继承CrudAppService.
示例:
@ -218,7 +218,9 @@ public interface IBookAppService :
}
````
* ICrudAppService有泛型参数来获取实体的主键类型和CRUD操作的DTO类型(它不获取实体类型,因为实体类型未向客户端公开使用此接口).
`ICrudAppService` 有泛型参数来获取实体的主键类型和CRUD操作的DTO类型(它不获取实体类型,因为实体类型未向客户端公开使用此接口).
> 为应用程序服务创建一个接口是最佳做法,但是ABP框架并不强制你这么做,你可以跳过接口部分.
`ICrudAppService`声明以下方法:
@ -291,7 +293,52 @@ public class BookAppService :
`CrudAppService`实现了`ICrudAppService`接口中声明的所有方法. 然后,你可以添加自己的自定义方法或覆盖和自定义实现.
### 生命周期
> `CrudAppService` 有不同数量泛型参数的版本,你可以选择适合的使用.
### AbstractKeyCrudAppService
`CrudAppService` 要求你的实体拥有一个Id属性做为主键. 如果你使用的是复合主键,那么你无法使用它.
`AbstractKeyCrudAppService` 实现了相同的 `ICrudAppService` 接口,但它没有假设你的主键.
#### 示例
假设你有实体 `District`,它的`CityId` 和 `Name` 做为复合主键,使用 `AbstractKeyCrudAppService` 时需要你自己实现 `DeleteByIdAsync``GetEntityByIdAsync` 方法:
````csharp
public class DistrictAppService
: AbstractKeyCrudAppService<District, DistrictDto, DistrictKey>
{
public DistrictAppService(IRepository<District> repository)
: base(repository)
{
}
protected override async Task DeleteByIdAsync(DistrictKey id)
{
await Repository.DeleteAsync(d => d.CityId == id.CityId && d.Name == id.Name);
}
应用服务的生命周期是[transient](Dependency-Injection)的,它们会自动注册到依赖注入系统.
protected override async Task<District> GetEntityByIdAsync(DistrictKey id)
{
return await AsyncQueryableExecuter.FirstOrDefaultAsync(
Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name)
);
}
}
````
这个实现需要你创建一个类做为复合键:
````csharp
public class DistrictKey
{
public Guid CityId { get; set; }
public string Name { get; set; }
}
````
### 生命周期
应用服务的生命周期是[transient](Dependency-Injection)的,它们会自动注册到依赖注入系统.

3
docs/zh-Hans/AspNet-Boilerplate-Migration-Guide.md

@ -0,0 +1,3 @@
# ASP.NET Boilerplate v5+ 迁移到 ABP Framework
TODO...

4
docs/zh-Hans/Authorization.md

@ -205,6 +205,10 @@ public class AuthorAppService : ApplicationService, IAuthorAppService
参阅 [基于策略的授权](https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/policies) 文档了解如何自定义策略.
### 更改依赖模块的权限定义
`PermissionDefinitionProvider` 派生的类(就像上面的示例一样) 可以获取现有的权限定义(由依赖[模块](Module-Development-Basics.md)定义)并更改其定义.
## IAuthorizationService
ASP.NET Core 提供了 `IAuthorizationService` 用于检查权限. 注入后使用它进行条件控制权限.

5
docs/zh-Hans/Background-Jobs.md

@ -175,4 +175,7 @@ public class MyModule : AbpModule
* [Hangfire 后台作业管理器](Background-Jobs-Hangfire.md)
* [RabbitMQ 后台作业管理器](Background-Jobs-RabbitMq.md)
* [Quartz 后台作业管理器](Background-Jobs-Quartz.md)
* [Quartz 后台作业管理器](Background-Jobs-Quartz.md)
## 另请参阅
* [后台工作者](Background-Workers.md)

137
docs/zh-Hans/Background-Workers.md

@ -1,3 +1,138 @@
# 后台工作者
TODO
## 介绍
背景工人在应用简单独立的线程在后台运行。一般来说,他们定期运行,以执行一些任务。例子;
后台工作者在应用程序后台运行的简单的独立线程,一般来说它们定期运行执行一些任务.例如;
* 后台工作者可以定期**删除过时的日志**.
* 后台工作者可以定期检查**不活跃的用户**并且向其**发送邮件**使用户继续使用你的应用程序.
## 创建一个后台工作者
后台工作者应该直接或间接的继承 `IBackgroundWorker` 接口.
> 后台工作者是[单例](Dependency-Injection.md)的. 所以实例化运行你的工作者类的单个实例.
### BackgroundWorkerBase
`BackgroundWorkerBase` 是创建后台工作者的简单方法.
````csharp
public class MyWorker : BackgroundWorkerBase
{
public override Task StartAsync(CancellationToken cancellationToken = default)
{
//...
}
public override Task StopAsync(CancellationToken cancellationToken = default)
{
//...
}
}
````
`StartAsync` 开始你的工作者(在应用程序启动时),`StopAsync` 停止它(在应用程序关闭时).
> 你可以直接实现 `IBackgroundWorker`, 但 `BackgroundWorkerBase` 提供了一些像 `Logger` 的常用属性.
### AsyncPeriodicBackgroundWorkerBase
假设我们要设置用户为不活跃用户(如果用户最近30天未登录应用程序).`AsyncPeriodicBackgroundWorkerBase` 类简化了创建定期工作者的过程,我们在下面的示例中使用它:
````csharp
public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
{
public PassiveUserCheckerWorker(
AbpTimer timer,
IServiceScopeFactory serviceScopeFactory
) : base(
timer,
serviceScopeFactory)
{
Timer.Period = 600000; //10 minutes
}
protected override async Task DoWorkAsync(
PeriodicBackgroundWorkerContext workerContext)
{
Logger.LogInformation("Starting: Setting status of inactive users...");
//Resolve dependencies
var userRepository = workerContext
.ServiceProvider
.GetRequiredService<IUserRepository>();
//Do the work
await userRepository.UpdateInactiveUserStatusesAsync();
Logger.LogInformation("Completed: Setting status of inactive users...");
}
}
````
* `AsyncPeriodicBackgroundWorkerBase` 使用 `AbpTimer`(线程安全定时器)对象来确定**时间段**. 我们可以在构造函数中设置了`Period` 属性。
* 它需要实现 `DoWorkAsync` 方法**执行**定期任务.
* 最好使用 `PeriodicBackgroundWorkerContext` **解析依赖** 而不是构造函数. 因为 `AsyncPeriodicBackgroundWorkerBase` 使用 `IServiceScope` 在你的任务执行结束时会对其 **disposed**.
* `AsyncPeriodicBackgroundWorkerBase` **捕获并记录**`DoWorkAsync` 方法抛出的 **异常**.
## 注册后台工作者
创建一个后台工作者后,你应该将其添加到 `IBackgroundWorkerManager`. 最常见的地方是模块类的 `OnApplicationInitialization` 方法:
````csharp
[DependsOn(typeof(AbpBackgroundWorkersModule))]
public class MyModule : AbpModule
{
public override void OnApplicationInitialization(
ApplicationInitializationContext context)
{
context.AddBackgroundWorker<PassiveUserCheckerWorker>();
}
}
````
`context.AddBackgroundWorker(...)` 是以下代码的简化扩展方法:
````csharp
context.ServiceProvider
.GetRequiredService<IBackgroundWorkerManager>()
.Add(
context
.ServiceProvider
.GetRequiredService<PassiveUserCheckerWorker>()
);
````
所以,它解析了给定的后台工作者并添加到 `IBackgroundWorkerManager`.
如果我们通常在 `OnApplicationInitialization` 添加工作者,但并不是强制的. 你可以在应用程序的任何地方注入 `IBackgroundWorkerManager` 并在运行时添加工作者. 在你的应用程序关闭时Background worker manager会释放所有已注册的后台工作者.
## Options
`AbpBackgroundWorkerOptions` 是用于设置后台工作者的选择. 目前只有一个选项:
* `IsEnabled` (默认值: true): 用于为你的应用程序启动或禁用后台工作者系统.
## 让应用程序始终运行
后台工作者只有在你的应用程序运行时才会工作. 如果你将后台作业托管在web应用程序中(这是默认行为),那么你应该确保你的web应用程序被配置为始终运行. 否则只有在你的应用程序正在运行时后台作业才会工作.
## 在集群运行
如果你在集群环境中运行同时运行应用程序的多个实现,这种情况下要小心,每个应用程序都运行相同的后台工作者,如果你的工作者在相同的资源上运行(例如处理相同的数据),那么可能会产生冲突.
如果这对你的工作者是一个问题,你有两个选项:
* 使用上面提到的 `AbpBackgroundWorkerOptions` 禁用其他的后台工作者系统,只保留一个实例.
* 所有的应用程序都禁用后台工作者系统,创建一个特殊的应用程序在一个服务上运行执行工作者.
## Quartz 集成
ABP框架的后台工作者系统可以很好的执行周期任务. 但是你可能需要使用更高级的任务调度,像[Quartz](https://www.quartz-scheduler.net/). 参阅社区贡献的[Quartz集成](Background-Workers-Quartz.md)
## 另请参阅
* [后台工作者的Quartz集成](Background-Workers-Quartz.md)
* [后台作业](Background-Jobs.md)

142
docs/zh-Hans/Blog-Posts/2020-03-19 v2_3_Release/Post.md

@ -0,0 +1,142 @@
# ABP框架v2.3.0已经发布!
在**新冠病毒**的日子里,我们发布了**ABP框架v2.3**, 这篇文章将说明本次发布**新增内容**和过去的两周**我们做了什么**.
## 关于新冠病毒和我们的团队
关于冠状病毒的状况**我们很难过**.在[Volosoft](https://volosoft.com/)的团队,我们有不同国家的**远程工作者**在自己家里工作.从上周开始,我们已经**完全开始在家远程工作**,包括我们的主要办公室的员工.
我们相信并祈祷人类会在很短的时间内克服这个问题.
## 关于发布周期
从ABP v2.1.0开始,我们开始**每两周**的周四发布功能版本.本次是该决定后的第3次发布,我们看到这种方式目前运转良好,并提高了我们的灵活性.
我们将继续每两周发布**功能版本**(如v2.4,v2.5).另外,如果需要我们会随时发布**热修复版本**(如v2.3.1,v2.3.2).
## ABP框架v2.3.0新增内容
我们已在这两周的开发周期内通过**393次提交**完成和合并了 **[104](https://github.com/abpframework/abp/milestone/30?closed=1)个issue和pull request**.
我将介绍这个版本加入的一些新功能和改善.
### React Native移动应用程序
我们终于完成了**react native移动应用程序**.目前,它可以让你**登录**,管理**用户**和**租户**.它利用ABP框架相同的设置,授权和本地化系统.
应用程序的一些截图:
![mobile-ui](react-native-ui.png)
它没有太多的功能,但它是你的移动应用程序一个**完美的起点**,因为它是完全集成到后端并支持多租户.
### Angular TypeScript代理生成器
从我们的Angular应用程序中调用服务器中的REST端点是很常见的.这种情况下,我们一般创建**服务**(在服务器上包含各个服务的方法)和**模型对象**(对应服务器上的[DTO](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects)).
除了手动创建这样的与服务器交互的服务外,我们可以使用像[NSWAG](https://github.com/RicoSuter/NSwag)工具来为我们生成服务代理.但是NSWAG有以下几个我们遇到的问题:
* 它产生一个**大,单一**的.ts文件;
* 当你的应用程序增长时,它变得**太大**了.
* 它不适合ABP框架的 **[模块化](https://docs.abp.io/en/abp/latest/Module-Development-Basics)方式**.
* 它创建了有点**丑陋的代码**.我们希望有一个干净的代码(就像我们手写的).
* 它不能生成服务器端声明的相同的**方法签名**(因为swagger.json不能准确地反映后端服务的方法签名).我们已创建了公开服务器端方法约定的端点,来允许客户端生成更好的客户端代理.
因此,我们决定创建一个ABP CLI命令来自动生成typescript客户端代理([#2222](https://github.com/abpframework/abp/issues/2222)),用于在ABP框架中开发REST API.
它用起来很简单.只需要在你Angular应用程序的**根文件夹**运行以下命令
````bash
abp generate-proxy
````
它只会为你自己的应用程序的服务创建代理.它(默认)不会为你使用的应用程序模块创建代理.有几个选项.参见[CLI文档](https://docs.abp.io/en/abp/latest/CLI).
### 复合主键的CRUD应用服务
` CrudAppService `是一个很有用的基类,用来为你的实体创建CRUD应用服务.不过,它不支持**复合主键**的实体. `AbstractKeyCrudAppService`是新开发的基类以支持复合主键的实体.更多信息请浏览[文档](https://docs.abp.io/en/abp/latest/Application-Services#abstractkeycrudappservice).
### 添加模块的源代码
应用程序启动模板带有一些[应用模块](https://docs.abp.io/en/abp/latest/Modules/Index), 以**Nuget和NPM包**的方式**预先安装了** .这样做有几个重要的优点:
* 当新版本可用时, 你可以 **轻松地[升级](https://docs.abp.io/en/abp/latest/CLI#update)** 这些模块.
* 你的解决方案**更干净**,这样你就可以专注于自己的代码.
但是,当你需要对一个依赖的模块**大量定制**时,就不如它的代码在你的应用程序中那么容易.为了解决这个问题,我们引入了一个[ABP CLI](https://docs.abp.io/en/abp/latest/CLI)的新命令, 在你的解决方案中用代码**替换**Nuget包.用法很简单:
````bash
abp add-module --with-source-code
````
该命令以源代码方式添加模块, 或者如果模块已经以包引用方式添加了, 则替换为源代码,.
> 建议在使用此命令前**保存你的更改**到源代码控制系统, 因为它会修改很多你的代码.
此外,我们也创建了文档来说明如何定制依赖的模块而不改变它们的源代码(见下面的部分).仍然建议以包的方式使用模块,以便在以后可以轻松升级.
> 免费模块的源代码是**MIT**许可,所以你可以自由更改它们并添加到您的解决方案中.
### 切换到预览版
ABP框架正在迅速发展,我们经常发布新版本.不过,如果你想更紧密地追随它,你可以使用**每日预览包**.
我们创建了一个ABP CLI命令来轻松地为你的解决方案**更新到最新的预览包**.在你的解决方案的根文件夹中运行以下命令:
````bash
abp switch-to-preview
````
它会修改所有ABP相关的NuGet和NPM包的版本.当你需要时你也可以**切换回最新稳定版**:
````bash
abp switch-to-stable
````
更多信息请浏览[ABP CLI文档](https://docs.abp.io/en/abp/latest/CLI#switch-to-preview).
### 文档改进
#### 扩展/定制依赖应用模块
我们创建了一个巨大的文档来说明如何定制模块依赖而不改变其源代码.参见[文档](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Guide).
除了文档以外,我们已经修订了所有模块([#3166](https://github.com/abpframework/abp/issues/3166)),来使他们的服务更容易扩展和定制.
#### EF Core迁移指南
最近,我们创建了一个指南,说明ABP启动模板所使用的迁移系统. [该指南](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Migrations)还介绍了如何定制迁移结构,拆分你的模块跨多个数据库,复用一个模块的表,等等.
#### 从 ASP.NET Boilerplate迁移
如果你有建立在 ASP.NET Boilerplate的解决方案,我们[创建了一个指南](https://docs.abp.io/en/abp/latest/AspNet-Boilerplate-Migration-Guide),试着帮助迁移你的解决方案到新的ABP框架上.
### 其他一些功能
#### 框架
* 添加`IRepository.GetAsync`和`IRepository.FindAsync`方法([#3184](https://github.com/abpframework/abp/issues/3148)).
#### 模块
* 当创建新租户时获取管理员的密码和电子邮件地址,租户管理模块([#3088](https://github.com/abpframework/abp/issues/3088)).
* 集成Elastic全文检索, 文档模块([#2901](https://github.com/abpframework/abp/pull/2901)).
* 新的Quartz后台工作者模块([#2762](https://github.com/abpframework/abp/issues/2762))
#### 示例
* 微服务演示添加多租户支持([#3032](https://github.com/abpframework/abp/pull/3032)).
所有的功能, 改善和BUG修复, 请浏览[发布说明](https://github.com/abpframework/abp/releases/tag/2.3.0).
## 下一步?
我们未来几个月的目标如下:
* 完成**文档和示例**,写更多的教程.
* 使框架和现有模块的更加**可定制和可扩展**.
* 集成**gRPC**和为所有预置模块实现gRPC端点([#2882](https://github.com/abpframework/abp/issues/2882)).
* 为ABP框架创建**Blazor UI**, 并在所有模块和启动模板中实现它([#394](https://github.com/abpframework/abp/issues/394)).
* 为预置模块**添加新功能**,并为[ABP商业版](https://commercial.abp.io/)创建新模块.
更多细节请浏览[GitHub里程碑](https://github.com/abpframework/abp/milestones).

BIN
docs/zh-Hans/Blog-Posts/2020-03-19 v2_3_Release/abp-io-release-2-3-0-blog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-03-19 v2_3_Release/react-native-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

34
docs/zh-Hans/CLI.md

@ -22,7 +22,7 @@ dotnet tool update -g Volo.Abp.Cli
生成基于ABP[启动模板](Startup-Templates/Index.md)的新解决方案.
基本用法:
用法:
````bash
abp new <解决方案名称> [options]
@ -48,6 +48,9 @@ abp new Acme.BookStore
* `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `none`: 无UI. 这个模板还有一些额外的选项:
* `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `--mobile` 或者 `-m`: 指定移动应用程序框架. 默认框架是 `react-native`. 其他选项:
* `none`: 不包含移动应用程序.
* `react-native`: React Native.
* `--database-provider` 或者 `-d`: 指定数据库提供程序.默认是 `ef`.其他选项:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
@ -56,6 +59,7 @@ abp new Acme.BookStore
* `--output-folder` 或者 `-o`: 指定输出文件夹,默认是当前目录.
* `--version` 或者 `-v`: 指定ABP和模板的版本.它可以是 [release tag](https://github.com/abpframework/abp/releases) 或者 [branch name](https://github.com/abpframework/abp/branches). 如果没有指定,则使用最新版本.大多数情况下,您会希望使用最新的版本.
* `--template-source` 或者 `-ts`: 指定自定义模板源用于生成项目,可以使用本地源和网络源(例如 `D\localTemplate``https://<your url>.zip`).
* `--create-solution-folder` 或者 `-csf`: 指定项目是在输出文件夹中的新文件夹中还是直接在输出文件夹中.
### add-package
@ -66,7 +70,7 @@ abp new Acme.BookStore
> 需要注意的是添加的模块可能需要额外的配置,通常会在包的文档中指出.
基本用法:
用法:
````bash
abp add-package <包名> [options]
@ -90,7 +94,7 @@ abp add-package Volo.Abp.MongoDB
> 由于分层,不同的数据库提供程序选项或其他原因,业务模块通常由多个包组成. 使用`add-module`命令可以大大简化向模块添加模块的过程. 但是每个模块可能需要一些其他配置,这些配置通常在相关模块的文档中指出.
基本用法:
用法:
````bash
abp add-module <模块名称> [options]
@ -148,7 +152,7 @@ abp switch-to-stable [options]
#### Options
`--solution-path``-sp`: 指定解决方案(.sln)文件路径. 如果未指定,CLI试寻找当前目录中的.sln文件.
`--solution-directory``-sd`: 指定解决方案文件夹. 解决方案应该在指定文件夹或子文件夹中. 如果未指定,默认为当前目录.
### login
@ -168,6 +172,28 @@ abp login <username>
abp logout
```
### generate-proxy
为你的HTTP API生成客户端代码,简化客户端使用服务的成本. 在运行 `generate-proxy` 命令之前,你的host必须启动正在运行.
用法:
````bash
abp generate-proxy [options]
````
#### Options
* `--apiUrl` 或者 `-a`:指定HTTP API的根URL. 如果未指定这个选项,默认使用你Angular应用程序的`environment.ts`文件API URL. 在运行 `generate-proxy` 命令之前,你的host必须启动正在运行.
* `--ui` 或者 `-u`: 指定UI框架,默认框架是angular.当前只有angular一个选项, 但我们会通过更改CLI增加新的选项. 尽请关注!
* `--module` 或者 `-m`:指定模块名. 默认模块名称为app. 如果你想所有模块,你可以指定 `--module all` 命令.
示例:
````bash
abp generate-proxy --apiUrl https://localhost:44305 --ui angular --module all
````
### help
CLI的基本用法信息.

148
docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md

@ -0,0 +1,148 @@
# 自定义应用模块: 扩展实体
在某些情况下你可能希望为依赖模块中定义的实体添加一些额外的属性(和数据库字段). 本节将介绍一些实现这一目标的不同方法.
## Extra Properties
[Extra properties](Entities.md)是一种存储实体的一些额外数据但不用更改实体的方式. 实体应该实现 `IHasExtraProperties` 接口. 所有预构建模块定义的聚合根实体都实现了 `IHasExtraProperties` 接口,所以你可以在这些实体中存储额外的属性.
示例:
````csharp
//SET AN EXTRA PROPERTY
var user = await _identityUserRepository.GetAsync(userId);
user.SetProperty("Title", "My custom title value!");
await _identityUserRepository.UpdateAsync(user);
//GET AN EXTRA PROPERTY
var user = await _identityUserRepository.GetAsync(userId);
return user.GetProperty<string>("Title");
````
这种方法开箱即用并且非常简单,你可以使用不同的属性名称(如这里的`Title`)在同一时间存储多个属性.
对于EF Core额外的属性被格式化成单个 `JSON` 字符值串存储在数据库中. 对于MongoDB它们做为单独的字段存储.
参阅[实体文档](Entities.md)了解更多关于额外系统.
> 可以基于额外的属性执行**业务逻辑**. 你可以**override**服务方法获取或设置值. 重写服务在下面进行讨论.
## 创建新实体映射到同一个数据库表/Collection
尽管额外属性方法**易于使用**并且适用于一些场景,但它具有[实体文档](Entities.md)中描述的一些缺点.
另一个方法是**创建你自己的实体**映射到**同一个数据库库**(对于MongoDB数据库是collection)
[应用程序启动模板](Startup-Templates/Application.md)的 `AppUser` 已经实现了这种方法. [EF Core迁移文档](Entity-Framework-Core-Migrations.md)描述了在这些情况下如何实现和管理**EF Core数据库迁移**. 这种方法同样适用于MongoDB,但你不需要处理数据库迁移问题.
## 创建一个拥有自己数据库表/Collection的新实体
映射你的实体到依赖模块的**已存在的表**有一些缺点;
* 你需要处理EF Core的**数据库迁移架构**. 需要特别注意迁移代码,特别是当你需要在实体间添加**关系**时.
* 你的应用程序数据库和模块数据库将是 **同一个物理数据库**. 通常需要时可以将模块数据库分开,但使用相同的表会对其进行限制.
如果你想要使你的实体或模块定义的实体**低耦合**,那么可以创建自己的数据库表/collection并且将你的实体映射到自己的数据库表.
在这种情况下你需要处理**同步问题**,尤其是你要**复制**相关实体的某些属性/字段时,有一些解决方案;
* 如果你构建的是一个 **单体** 应用程序(或者在同一进程管理你的实体和依赖模块的实体),那么你可以使用[本地事件总线](Local-Event-Bus.md)监听实体更改.
* 如果你构建的是一个 **分布式** 系统,模块的实体和你的实体在不同的 进程/服务 管理(创建/更新/删除),那么你可以使用[分布式事件总线](Distributed-Event-Bus.md)订阅实体的更改事件.
在你处理事件时,你可以在自己的数据库中更改自己的实体.
### 订阅本地事件总线
[本地事件总线](Local-Event-Bus.md)系统是发布和订阅同一应用程序中发生的事件的方法.
假设你想要获取 `IdentityUser` 实体的更改信息(创建,更改或删除). 你可以创建一个类实现 `ILocalEventHandler<EntityChangedEventData<IdentityUser>>` 接口.
````csharp
public class MyLocalIdentityUserChangeEventHandler :
ILocalEventHandler<EntityChangedEventData<IdentityUser>>,
ITransientDependency
{
public async Task HandleEventAsync(EntityChangedEventData<IdentityUser> eventData)
{
var userId = eventData.Entity.Id;
var userName = eventData.Entity.UserName;
//...
}
}
````
* `EntityChangedEventData<T>` 涵盖了给定实体的创建,更新或删除事件. 如果你需要你可以分别订阅创建,更新或删除事件(在同一个类或不同的类中).
* 这里的代码在**本地事务之外执行**,因为它监听 `EntityChanged` 事件. 如果当前[工作单元](Unit-Of-Work.md)是事务性的,你可以订阅 `EntityChangingEventData<T>` 事件,它在**同一本地(进行)事务**中执行事件处理.
> 提醒:这些方法需要在包含处理类的同一进程中更改 `IdentityUser` 实体. 即使在集群环境(同一应用程序的多个实例在不同的服务器进行),它也完美工作.
### 订阅分布式事件总线
[分布式事件总线](Distributed-Event-Bus.md)是在一个应用程序中发布事件,并在相同服务器或不同服务器运行的相同应用程序或不同应用程序中接收事件的方法.
假设你想要获取 `IdentityUser` 实体的创建,更改或删除信息. 你可以像以下一样创建一个类:
````csharp
public class MyDistributedIdentityUserChangeEventHandler :
IDistributedEventHandler<EntityCreatedEto<EntityEto>>,
IDistributedEventHandler<EntityUpdatedEto<EntityEto>>,
IDistributedEventHandler<EntityDeletedEto<EntityEto>>,
ITransientDependency
{
public async Task HandleEventAsync(EntityCreatedEto<EntityEto> eventData)
{
if (eventData.Entity.EntityType == "Volo.Abp.Identity.IdentityUser")
{
var userId = Guid.Parse(eventData.Entity.KeysAsString);
//...handle the "created" event
}
}
public async Task HandleEventAsync(EntityUpdatedEto<EntityEto> eventData)
{
if (eventData.Entity.EntityType == "Volo.Abp.Identity.IdentityUser")
{
var userId = Guid.Parse(eventData.Entity.KeysAsString);
//...handle the "updated" event
}
}
public async Task HandleEventAsync(EntityDeletedEto<EntityEto> eventData)
{
if (eventData.Entity.EntityType == "Volo.Abp.Identity.IdentityUser")
{
var userId = Guid.Parse(eventData.Entity.KeysAsString);
//...handle the "deleted" event
}
}
}
````
* 它实现了多个 `IDistributedEventHandler` 接口: **创建**,**更改**和**删除**,因为分布式事件总线单独发布事件,没有本地事件总线那样的"Changed"事件.
* 它订阅了 `EntityEto`, 这是一个通用的事件类,ABP框架针对所有类型的实体**自动发布**. 这就是为什么它检查**实体类型**(因为我们没有假设有对 `IdentityUser` 实体有安全的类型引用,所以它是字符串类型的).
预构建应用模块没有定义专门的事件类型(如`IdentityUserEto` - "ETO" 意思是 "事件传输对象"). 此功能在路线图上([关注这个issue](https://github.com/abpframework/abp/issues/3033)),一旦完成后,你就可以订阅独立的实体类型:
````csharp
public class MyDistributedIdentityUserCreatedEventHandler :
IDistributedEventHandler<EntityCreatedEto<IdentityUserEto>>,
ITransientDependency
{
public async Task HandleEventAsync(EntityCreatedEto<IdentityUserEto> eventData)
{
var userId = eventData.Entity.Id;
var userName = eventData.Entity.UserName;
//...handle the "created" event
}
//...
}
````
* 这个处理程序只会在新用户创建时执行.
> 唯一预定义的专门事件类是 `UserEto`, 你可以订阅 `EntityCreatedEto<UserEto>` 获取用户创建时的通知. 此事件也适用于身份模块.
## 另请参阅
* [自定义已存在的模块](Customizing-Application-Modules-Guide.md)

62
docs/zh-Hans/Customizing-Application-Modules-Guide.md

@ -0,0 +1,62 @@
# 自定义现有模块
ABP框架提供的设计旨在支持构建完全[模块化的应用程序](Module-Development-Basics.md)和系统. 它还提供了一些可以在任何类型的应用程序中**使用**的[预构建应用模块](Modules/Index.md)
例如,你可以在你的应用程序中**重用**[身份管理模块](Modules/Identity.md)去添加用户,角色和权限管理. [应用程序启动模板](Startup-Templates/Application.md)已经**预装**了Identity和其他模块.
## 复用应用模块
你有两个选项去复用应用模块:
### 添加包引用
你可以添加相关模块的 **NuGet****NPM** 包引用到你的应用程序,并配置模块(根据它的文档)集成到你的应用程序中.
正如前面提到,[应用程序启动模板](Startup-Templates/Application.md)已经**预装了一些基本模块**,它引用模块的NuGet和NPM包.
这种方法具有以下优点:
* 你的解决方案会非常**干净**,只包含你**自己的应用程序代码**.
* 你可以**很简单的**升级模块到最新的可用模板. `abp update` [CLI](CLI.md) 命令会使更新变的更加简单. 通过这种方式, 你可以获得**最新功能和Bus修复**.
然而有一个缺点:
* 你可能无法**自定义**模块,因为模块源码没有在你的解决方案中.
本文档介绍了 **或者自定义或扩展** 依赖模块并且无需更改其源码,尽快与更改完整的源码比起是有限的,但仍有一些好的方法可以自定义.
如果你不认为自己会对预构建的模块进行重大更改,那么使用包引用的方法复用模块是推荐的方法.
### 包含源码
如果你想要在预构建的模块上进行**重大**更改或添加**主要功能**,但是可用的扩展点不够使用,那么可以考虑直接使用依赖模块的源码.
这种情况下,你通常**添加模块源码**到你的解决方案中,并将**包引用替换**为本地项目引用. **[ABP CLI](CLI.md)** 可以为你自动化这一过程.
#### 分离模块解决方案
你可能不希望将模块源代码**直接包含在解决方案**中. 每个模块都包含十多个项目文件,添加**多个模块**会使解决方案变的臃肿可能还会影响**开发时的加载速度**,另外你可能有不同的开发团队维护不同模块.
无论如何,你都可以为需要的模块创建**单独的解决方案**,将依赖模块做为解决方案中的项目引用. 比如在[abp仓库](https://github.com/abpframework/abp/),我们就是这样做的.
> 我们看到的一个问题是Visual Studio在这种方式下不能很好的工作(解决方案目录之外对本地项目的引用不能很好地支持). 如果在开发过程中出错(对于外部模块),请在Visual Studio打开应用程序的解决方案后,在命令行运行 `dotnet restore`命令.
#### 发布的自定义模块的包
一个备选方案是将重新打包模块的源代码(NuGet/NPM包),使用包引用. 你可以为公司使用本地私人的Nuget/NPM服务器.
## 模块自定义/扩展途径
如果你决定使用预构建模块的NuGet/NPM包引用方式. 下面的文档详细解释了如何自定义/扩展现有模块的方法:
* [扩展实体](Customizing-Application-Modules-Extending-Entities.md)
* [重写服务](Customizing-Application-Modules-Overriding-Services.md)
* [重写界面](Customizing-Application-Modules-Overriding-User-Interface.md)
### 另请参阅
另外,请参阅以下文档:
* 参阅 [本地化文档](Localization.md) 学习如何扩展已存在的本地化资源.
* 参阅 [设置文档](Settings.md) 学习如何更改依赖模块的设置定义.
* 参阅 [授权文档](Authorization.md) 学习如何更改依赖模块的权限定义.

166
docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md

@ -0,0 +1,166 @@
# 自定义应用模块: 重写服务
你可能想要**更改**依赖模块的**行为(业务逻辑)**. 在这种情况下,你可以使用[依赖注入](Dependency-Injection.md)的能力替换服务,控制器甚至页面模型到你自己的实现.
注册到依赖注入的任何类,包括ABP框架的服务都可以被**替换**.
你可以根据自己的需求使用不同的选项,下面的章节中将介绍这些选项.
> 请注意,某些服务方法可能不是virtual,你可能无法override,我们会通过设计将其virtual,如果你发现任何方法不可以被覆盖,请[创建一个issue](https://github.com/abpframework/abp/issues/new)或者你直接修改后并发送**pull request**到GitHub.
## 替换接口
如果给定的服务定义了接口,像 `IdentityUserAppService` 类实现了 `IIdentityUserAppService` 接口,你可以为这个接口创建自己的实现并且替换当前的实现. 例如:
````csharp
public class MyIdentityUserAppService : IIdentityUserAppService, ITransientDependency
{
//...
}
````
`MyIdentityUserAppService` 通过命名约定替换了 `IIdentityUserAppService` 的当前实现. 如果你的类名不匹配,你需要手动公开服务接口:
````csharp
[ExposeServices(typeof(IIdentityUserAppService))]
public class TestAppService : IIdentityUserAppService, ITransientDependency
{
//...
}
````
依赖注入系统允许为一个接口注册多个服务. 注入接口时会解析最后一个注入的服务. 显式的替换服务是一个好习惯.
示例:
````csharp
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IIdentityUserAppService))]
public class TestAppService : IIdentityUserAppService, ITransientDependency
{
//...
}
````
使用这种方法, `IIdentityUserAppService` 接口将只会有一个实现. 也可以使用以下方法替换服务:
````csharp
context.Services.Replace(
ServiceDescriptor.Transient<IIdentityUserAppService, MyIdentityUserAppService>()
);
````
你可以在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法编写替换服务代码.
## 重写一个服务类
大多数情况下,你会仅想改变服务当前实现的一个或几个方法. 重新实现完整的接口变的繁琐,更好的方法是继承原始类并重写方法。
### 示例: 重写服务方法
````csharp
[Dependency(ReplaceServices = true)]
public class MyIdentityUserAppService : IdentityUserAppService
{
//...
public MyIdentityUserAppService(
IdentityUserManager userManager,
IIdentityUserRepository userRepository,
IGuidGenerator guidGenerator
) : base(
userManager,
userRepository,
guidGenerator)
{
}
public override async Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input)
{
if (input.PhoneNumber.IsNullOrWhiteSpace())
{
throw new AbpValidationException(
"Phone number is required for new users!",
new List<ValidationResult>
{
new ValidationResult(
"Phone number can not be empty!",
new []{"PhoneNumber"}
)
}
); }
return await base.CreateAsync(input);
}
}
````
示例中**重写**了 `IdentityUserAppService` [应用程序](Application-Services.md) `CreateAsync` 方法检查手机号码. 然后调用了基类方法继续**基本业务逻辑**. 通过这种方法你可以在基本业务逻辑**之前**和**之后**执行其他业务逻辑.
你也可以完全**重写**整个业务逻辑去创建用户,而不是调用基类方法.
### 示例: 重写领域服务
````csharp
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IdentityUserManager))]
public class MyIdentityUserManager : IdentityUserManager
{
public MyIdentityUserManager(
IdentityUserStore store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<IdentityUser> passwordHasher,
IEnumerable<IUserValidator<IdentityUser>> userValidators,
IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<IdentityUserManager> logger,
ICancellationTokenProvider cancellationTokenProvider
) : base(
store,
optionsAccessor,
passwordHasher,
userValidators,
passwordValidators,
keyNormalizer,
errors,
services,
logger,
cancellationTokenProvider)
{
}
public override async Task<IdentityResult> CreateAsync(IdentityUser user)
{
if (user.PhoneNumber.IsNullOrWhiteSpace())
{
throw new AbpValidationException(
"Phone number is required for new users!",
new List<ValidationResult>
{
new ValidationResult(
"Phone number can not be empty!",
new []{"PhoneNumber"}
)
}
);
}
return await base.CreateAsync(user);
}
}
````
示例中类继承了 `IdentityUserManager` [领域服务](Domain-Services.md),并且重写了 `CreateAsync` 方法进行了与之前相同的手机号码检查. 结果也是一样的,但是这次我们在领域服务实现了它,假设这是我们系统的**核心领域逻辑**.
> 这里需要 `[ExposeServices(typeof(IdentityUserManager))]` attribute,因为 `IdentityUserManager` 没有定义接口 (像 `IIdentityUserManager`) ,依赖注入系统并不会按照约定公开继承类的服务(如已实现的接口).
参阅[本地化系统](Localization.md)了解如何自定义错误消息.
### 重写其他服务
控制器,框架服务,视图组件类以及其他类型注册到依赖注入的类都可以像上面的示例那样被重写.
## 如何找到服务?
[模块文档](Modules/Index.md) 包含了定义的主要服务列表. 另外 你也可以查看[源码](https://github.com/abpframework/abp/tree/dev/modules)找到所有的服务.

6
docs/zh-Hans/Customizing-Application-Modules-Overriding-User-Interface.md

@ -0,0 +1,6 @@
# 重写用户界面
你可以想要重写页面,组件,JavaScript,CSS或你依赖模块的图片文件. 重写UI取决于你使用的UI框架. 选择UI框架以继续:
* [ASP.NET Core (MVC / Razor Pages)](UI/AspNetCore/Customization-User-Interface.md)
* [Angular](UI/Angular/Customization-User-Interface.md)

16
docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md

@ -91,4 +91,18 @@ builder.ConfigureIdentityServer(options =>
## 运行应用程序
它已准备就绪, 只需要运行该应用程序与享受编码.
它已准备就绪, 只需要运行该应用程序与享受编码.
## DBMS限制
不同的DBMS可能存在一些限制, 如字段名称的最大长度, 索引长度等等.
模块可能提供了一些内置的解决方案. 你可以通过`ModelBuilder`来配置它 如: Identity Server模块
```csharp
builder.ConfigureIdentityServer(options =>
{
options.DatabaseProvider = EfCoreDatabaseProvider.MySql;
});
```
相关讨论: https://github.com/abpframework/abp/issues/1920

140
docs/zh-Hans/Modules/Docs.md

@ -26,15 +26,19 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块.
如果你没有现有的ABP项目, 这个步骤向你展示如何在[abp.io](https://abp.io)创建一个新项目并添加文档模块. 如果你本地已经有了一个ABP项目, 那么你可以跳过这一步.
打开 https://abp.io/Templates. 输入项目名称为 `Acme.MyProject`, 选择 `ASP.NET Core Mvc Application` 和选择 `Entity Framework Core` 做为数据库提供者.
推荐使用ABP CLI创建新项目,使用以下命令行:
请注意,本文档包含了 `Entity Framework Core` 提供者 不过你也可以选择 `MongoDB` 做为数据库提供者.
`abp new Acme.MyProject`
你也可以在浏览器中导航到 https://abp.io/get-started. 输入项目名称为 `Acme.MyProject`, 其它保持默认选项.
请注意,本文档包含了 `Entity Framework Core` 提供者 不过你也可以选择 `MongoDB` 做为数据库提供者.
![创建新项目](../images/docs-module_download-new-abp-project.png)
### 2- 运行这个空项目
下载项目后, 解压压缩文档并且打开 `Acme.MyProject.sln`. 你可以看到这个解决方案包含了 `Application`, `Domain`, `EntityFrameworkCore``Web` 项目. 右键选择 `Acme.MyProject.Web` 项目**设置为启动项目**.
下载项目后, 解压压缩文档并且打开 `Acme.MyProject.sln`. 你可以看到这个解决方案包含了 `Application`, `Application.Contrawcts`, `DbMigrator`, `Domain`, `Domain.Shared`, `EntityFrameworkCore`, `EntityFrameworkCore.DbMigations`, `HttpApi`, `HttpApi.Client``Web` 项目. 右键选择 `Acme.MyProject.Web` 项目**设置为启动项目**.
![创建新项目](../images/docs-module_solution-explorer.png)
@ -43,12 +47,12 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块.
```json
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true"
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
```
打开Visual Studio包管理控制台选择`src\Acme.MyProject.EntityFrameworkCore` 做为默认项目. 运行 `Update-Database` 命令创建数据库. 数据库`MyProject`将在数据库服务器中创建.
运行 `Acme.MyProject.DbMigrator` 项目,它会负责应用迁移与初始化种子数据. 数据库`MyProject`将在数据库服务器中创建.
现在一个空的ABP项目已经创建完成! 现在你可以运行项目并且查看网站.
@ -56,39 +60,28 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块.
### 2- 引用文档模块包
文档模块包托管在Nuget上面. 需要有四个包安装到你的应用程序中. 每个包必须安装到相关的项目.
* [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) 需要安装到 `Acme.MyProject.Domain` 项目.
文档模块包托管在Nuget上面. 需要有四个包安装到你的应用程序中. 每个包必须安装到相关的项目.
* 修改 `Acme.MyProject.Domain.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本.
建议使用ABP CLI安装模块,在解决方案文件 (`.sln`) 目录打开 `CMD` 窗口,运行以下命令:
```csharp
<PackageReference Include="Volo.Docs.Domain" Version="0.9.0" />
```
`abp add-module Volo.Docs`
* [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) 需要安装到 `Acme.MyProject.EntityFrameworkCore` 项目.
或者你也可以手动安装nuget包到每个项目:
* 修改 `Acme.MyProject.EntityFrameworkCore.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本.
* 安装[Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget包到 `Acme.MyProject.Domain` 项目.
```csharp
<PackageReference Include="Volo.Docs.EntityFrameworkCore" Version="0.9.0" />
```
`Install-Package Volo.Docs.Domain`
* [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) 需要安装到 `Acme.MyProject.Application` 项目.
* 安装[Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) nuget包到 `Acme.MyProject.EntityFrameworkCore` 项目.
* 修改 `Acme.MyProject.Application.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本.
`Install-Package Volo.Docs.EntityFrameworkCore`
```csharp
<PackageReference Include="Volo.Docs.Application" Version="0.9.0" />
```
* 安装[Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) nuget包到 `Acme.MyProject.Application` 项目.
* [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Web/) 需要安装到 `Acme.MyProject.Web` 项目.
`Install-Package Volo.Docs.Application`
* 修改 `Acme.MyProject.Web.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本.
```csharp
<PackageReference Include="Volo.Docs.Web" Version="0.9.0" />
```
* 安装[Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget包到 `Acme.MyProject.Web` 项目.
`Install-Package Volo.Docs.Web`
### 3- 添加模块依赖
@ -176,37 +169,58 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块.
#### 4.1- Entity Framework 集成
如果你选择了Entity Framework 做为数据库供应者,你需要在DbContext中配置文档模块. 做以下操作;
如果你选择了Entity Framework 做为数据库供应者,你需要配置文档模块. 做以下操作;
* 打开 `MyProjectDbContext.cs` 并且添加 `modelBuilder.ConfigureDocs()``OnModelCreating()` 方法中
* 打开 `MyProjectMigrationsDbContext.cs` 并且添加 `builder.ConfigureDocs()``OnModelCreating()` 方法中
```csharp
[ConnectionStringName("Default")]
public class MyProjectDbContext : AbpDbContext<MyProjectDbContext>
{
public MyProjectDbContext(DbContextOptions<MyProjectDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//...
modelBuilder.ConfigureDocs();
}
}
public class MyProjectMigrationsDbContext : AbpDbContext<MyProjectMigrationsDbContext>
{
public MyProjectMigrationsDbContext(DbContextOptions<MyProjectMigrationsDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureBackgroundJobs();
builder.ConfigureAuditLogging();
builder.ConfigureIdentity();
builder.ConfigureIdentityServer();
builder.ConfigureFeatureManagement();
builder.ConfigureTenantManagement();
builder.ConfigureDocs(); //Add this line to configure the Docs Module
/* Configure customizations for entities from the modules included */
builder.Entity<IdentityUser>(b =>
{
b.ConfigureCustomUserProperties();
});
/* Configure your own tables/entities inside the ConfigureQaDoc method */
builder.ConfigureMyProject();
}
}
```
* 打开 `Visual Studio``包管理控制台` 选择 `Acme.MyProject.EntityFrameworkCore` 做为默认项目. 然后编写以下命令为文档模块添加迁移.
* 打开 `Visual Studio``包管理控制台` 选择 `Acme.MyProject.EntityFrameworkCore.DbMigrations` 做为默认项目. 然后编写以下命令为文档模块添加迁移.
```csharp
add-migration Added_Docs_Module
```
当命令执行成功后 , 你会看到`Acme.MyProject.EntityFrameworkCore\Migrations` 目录下有名为 `20181221111621_Added_Docs_Module` 的迁移文件.
当命令执行成功后 , 你会看到`Acme.MyProject.EntityFrameworkCore.DbMigrations\Migrations` 目录下有名为 `20181221111621_Added_Docs_Module` 的迁移文件.
现在更新数据库. 在 `Visual Studio``包管理控制台` 中执行以下代码. 要确认已 `Acme.MyProject.EntityFrameworkCore` 项目设置为默认项目.
现在更新数据库. 在 `Visual Studio``包管理控制台` 中执行以下代码. 要确认已 `Acme.MyProject.EntityFrameworkCore.DbMigrations` 项目设置为默认项目.
```csharp
update-database
@ -309,7 +323,7 @@ There are no projects yet!
- ExtraProperties:
```json
{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/zh-Hans/","GitHubAccessToken":"***"}
{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/zh-Hans/","GitHubAccessToken":"***","GitHubUserAgent":""}
```
注意 `GitHubAccessToken``***` 掩盖. 这是一个私人令牌,你必须从GitHub获取它. 请参阅 https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
@ -321,11 +335,13 @@ There are no projects yet!
对于 `SQL` 数据库,你可以使用下面的 `T-SQL` 命令将指定的示例插入到 `DocsProjects` 表中:
```mssql
INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***"}', N'/', N'master', N'')
INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***","GitHubUserAgent":""}', N'/', N'master', N'')
```
请注意,`GitHubAccessToken` 被屏蔽了.它是一个私人令牌,你必须获得自己的令牌并替换 `***` 字符串.
现在你可以运行应用程序并导航到 `/Documents`.
#### "FileSystem" 项目的示例记录
你可以使用 [ABP Framework](https://github.com/abpframework/abp/) GitHub文档来配置你的文件系统存储.
@ -556,6 +572,30 @@ This document assumes that you prefer to use **{{ UI_Value }}** as the UI framew
最后,为您的项目添加了一个新的Docs模块, 该模块由GitHub提供.
## 全文搜索(Elastic Search)
文档模块支持使用Elastic Search对内容进行全文搜索. 默认没有启用, 你可以配置`DocsElasticSearchOptions`启用它.
```
Configure<DocsElasticSearchOptions>(options =>
{
options.Enable = true;
options.IndexName = "your_index_name"; //default IndexName is abp_documents
});
```
应用程序启动后如果`Index`不存在则会自动创建`Index`.
`DefaultElasticClientProvider`负责创建`IElasticClient`, 默认情况下它会从`IConfiguration`中读取Elastic Search的`Url`.
如果你的IElasticClient需要其它配置请使用重写IElasticClientProvider服务并在依赖注入系统中替换它.
```
{
"ElasticSearch": {
"Url": "http://localhost:9200"
}
}
```
## 下一步
文档模块也可以做为独立的应用程序. 查看 [VoloDocs](../Apps/VoloDocs).

6
docs/zh-Hans/Multi-Tenancy.md

@ -343,8 +343,8 @@ namespace MyCompany.MyProject
{
Configure<AbpTenantResolveOptions>(options =>
{
//子域名格式: {0}.mydomain.com (作为最高优先级解析器添加)
options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));
//子域名格式: {0}.mydomain.com (作为第二优先级解析器添加, 位于CurrentUserTenantResolveContributor之后)
options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));
});
//...
@ -355,7 +355,7 @@ namespace MyCompany.MyProject
{0}是用来确定当前租户唯一名称的占位符.
你可以使用下面的方法,代替``options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));``:
你可以使用下面的方法,代替``options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));``:
````C#
options.AddDomainTenantResolver("{0}.mydomain.com");

91
docs/zh-Hans/Samples/Microservice-Demo.md

@ -24,82 +24,15 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服
下图展示了该系统:
![microservice-sample-diagram](../images/microservice-sample-diagram-2.png)
![microservice-sample-diagram](../images/microservice-sample-diagram-3.png)
### 源码
你可以从[GitHub仓库](https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo)获取源码.
### 状态
该示例的初始版本已完成.其他改进仍在开发中.
## 运行解决方案
您可以从 **源代码** 或者预先配置好的 **docker-compose** 文件运行.
### 使用Docker容器
#### 预先要求
由于所有依赖项都已预先配置, 因此作为Docker容器运行更容易. 你只需要安装[最新的docker](https://docs.docker.com/compose/install/).
#### 运行容器
- 克隆或下载 [ABP仓库](https://github.com/abpframework/abp).
- 在存储库的`samples/MicroserviceDemo`文件夹中打开命令行.
- 从Docker Hub中拉取image:
```
docker-compose -f docker-compose.yml -f docker-compose.migrations.yml pull
```
- 如果要在本地构建映像, 可以跳过上述步骤, 使用build命令:
```
docker-compose -f docker-compose.yml -f docker-compose.migrations.yml build
```
根据你的电脑配置, 构建image可能需要**很长时间**.
- 还原 SQL Server 数据库:
```
docker-compose -f docker-compose.yml -f docker-compose.migrations.yml run restore-database
```
- 启动容器:
```
docker-compose up -d
```
- 将此行添加到`hosts`文件的末尾:
```
127.0.0.1 auth-server
```
hosts文件位于Windows上的`C:\Windows\System32\Drivers\etc\hosts`文件夹, Linux/MacOS的`/etc/hosts`中.
#### 运行应用程序
你可能想要了解容器中运行一些应用程序:
* 后端管理应用程序 (BackendAdminApp.Host): `http://localhost:51512`
*(用于管理系统中的用户和产品)*
* 公共网站 (PublicWebsite.Host): `http://localhost:51513`
*(用于列出产品并运行/管理博客模块)*
* 认证服务器 (AuthServer.Host): `http://auth-server:51511/`
*(用作使用IdentityServer4构建的单点登录和身份验证服务器)*
* Kibana UI: `http://localhost:51510`
*(用于显示/跟踪所有服务/应用程序/网关写入的日志)*
### 从源代码运行
#### 预先要求
### 预先要求
为了能够从源代码运行解决方案, 应在你的计算机上安装并运行以下工具:
@ -110,19 +43,19 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服
* [ElasticSearch](https://www.elastic.co/downloads/elasticsearch) 6.6+
* [Kibana](https://www.elastic.co/downloads/kibana) 6.6+ (可选,建议显示日志)
#### 打开并构建Visual Studio解决方案
### 打开并构建Visual Studio解决方案
* 在Visual Studio 2017 (15.9.0+)中打开`samples\MicroserviceDemo\MicroserviceDemo.sln`.
* 在`samples\MicroserviceDemo`文件夹中的命令行运行`dotnet restore`命令.
* 在Visual Studio中构建解决方案.
#### 创建数据库
### 创建数据库
MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库的结构。其实你可以很轻松的创建数据库,因为这个解决方案配置了使用 Entity Core Code First 来做迁移。
这个解决方案中有两个 SQL server 数据库。
##### MsDemo_Identity 数据库
#### MsDemo_Identity 数据库
* 右键 `AuthServer.Host` 项目,然后点击 `设置为启动项目`.
* 打开 **程序包管理器控制台** (工具 -> NuGet 包管理器 -> 程序包管理器控制台)
@ -131,7 +64,7 @@ MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库
![microservice-sample-update-database-authserver](../images/microservice-sample-update-database-authserver.png)
##### MsDemo_ProductManagement
#### MsDemo_ProductManagement
* 右键 `ProductService.Host` 项目,然后点击 `设置为启动项目`.
* 打开 **程序包管理器控制台** (工具 -> NuGet 包管理器 -> 程序包管理器控制台)
@ -140,12 +73,13 @@ MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库
![microservice-sample-update-database-products](../images/microservice-sample-update-database-products.png)
#### 运行项目
### 运行项目
按以下顺序运行项目(右键单击每个项目设置为启动项目,按Ctrl+F5运行,无需调试):
* AuthServer.Host
* IdentityService.Host
* TenantManagementService.Host
* BloggingService.Host
* ProductService.Host
* InternalGateway.Host
@ -158,7 +92,7 @@ MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库
Visual Studio解决方案由多个项目组成,每个项目在系统中具有不同的角色:
![microservice-sample-solution](../images/microservice-sample-solution.png)
![microservice-sample-solution](../images/microservice-sample-solution-2.png)
### 应用程序(Applications)
@ -182,6 +116,7 @@ Visual Studio解决方案由多个项目组成,每个项目在系统中具有不
微服务没有UI,但暴露了一些REST API.
- **IdentityService.Host**: 托管用于管理用户和角色的ABP Identity模块. 它没有其他服务,仅托管Identity模块的API.
- **TenantManagementService.Host**: 托管用于管理角色的ABP租户管理模块. 它没有其他服务,仅托管租户管理模块的API.
- **BloggingService.Host**: 托管ABP博客模块,该模块用于管理博客和帖子(典型的博客应用程序). 它没有其他服务,仅托管Blogging模块的API.
- **ProductService.Host**: 托管用于管理产品的产品模块(位于解决方案内). 它还包含用于创建/更新产品管理数据库架构的EF Core迁移.
@ -193,7 +128,7 @@ Visual Studio解决方案由多个项目组成,每个项目在系统中具有不
此解决方案使用多个数据库:
* **MsDemo_Identity**: 一个SQL数据库. 默认使用**SQL Server**,但可以是EF Core支持的任何DBMS. 由AuthServer和IdentityService共享. 审计日志,权限和设置也存储在此数据库中(虽然它们可以轻松拥有自己的数据库,共享相同的数据库以保持简单).
* **MsDemo_Identity**: 一个SQL数据库. 默认使用**SQL Server**,但可以是EF Core支持的任何DBMS. 由AuthServer,IdentityService和TenantManagementService共享. 审计日志,权限和设置也存储在此数据库中(虽然它们可以轻松拥有自己的数据库,共享相同的数据库以保持简单).
* **MsDemo_ProductManagement**: 一个SQL数据库. 同样默认使用 **SQL Server**,但可以是EF Core支持的任何DBMS. 由ProductService用作专用数据库.
* **MsDemo_Blogging**: **MongoDB**数据库. 由BloggingService使用.
* **Elasticsearch**: 用于在Serilog上写日志.
@ -1474,3 +1409,7 @@ ABP提供自动审计日志记录,详细保存每个请求(当前用户,浏览
所有服务和应用程序都配置为编写审核日志. 审核日志将保存到MsDemo_Identity SQL数据库中. 因此,您可以从单个点查询所有应用程序的所有审核日志.
审核日志记录具有`CorrelationId`属性,可用于跟踪请求. 当服务在单个Web请求中调用另一个服务时,它们都会使用相同的`CorrelationId`保存审核日志. 请参阅数据库中的`AbpAuditLogs`表.
### 多租户
该解决方案已配置提供[多租户](../Multi-Tenancy.md)系统,其中每个租户可以拥有其隔离的用户,角色,权限和其他数据.

6
docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md

@ -52,7 +52,7 @@ namespace MyCompany.MyProject
* 当首次请求时,ABP从提供的文件中 **(延迟)lazy** 创建. 后续将从 **缓存** 中返回内容. 这意味着如果你有条件地将文件添加到包中,它只执行一次, 并且条件的任何更改都不会影响下一个请求的包.
* 在`development`环境中ABP会将包文件**单独**添加到页面中, 其他环境(`staging`,`production`...)会自动捆绑和压缩.
* 捆绑文件可以是**物理**文件或[**虚拟/嵌入**](../Virtual-File-System.md)的文件.
* 捆绑文件可以是**物理**文件或[**虚拟/嵌入**](../../Virtual-File-System.md)的文件.
* ABP自动将 **版本查询字符串(version query string)** 添加到捆绑文件的URL中,以防止浏览器缓存. 如:?_v=67872834243042(从文件的上次更改日期生成). 即使捆绑文件单独添加到页面(在`development`环境中), 版本控制仍然有效.
#### 导入 Bundling Tag Helpers
@ -103,7 +103,7 @@ namespace MyCompany.MyProject
### Bundling 选项
如果你需要在 **多个页面中使用相同的包** 或想要使用更多 **强大功能**, 你可以在[模块](../Module-Development-Basics.md)类中进行**配置**.
如果你需要在 **多个页面中使用相同的包** 或想要使用更多 **强大功能**, 你可以在[模块](../../Module-Development-Basics.md)类中进行**配置**.
#### 创建一个新的捆绑包
@ -144,7 +144,7 @@ public class MyWebModule : AbpModule
#### 配置现有的 Bundle
ABP也支持[模块化](../Module-Development-Basics.md)捆绑. 模块可以修改由依赖模块创建的捆绑包.
ABP也支持[模块化](../../Module-Development-Basics.md)捆绑. 模块可以修改由依赖模块创建的捆绑包.
例如:
````C#

3
docs/zh-Hans/UI/AspNetCore/Customization-User-Interface.md

@ -0,0 +1,3 @@
# ASP.NET Core (MVC / Razor Pages) 用户界面自定义指南
TODO...

3
docs/zh-Hans/UI/AspNetCore/Layout-Hooks.md

@ -0,0 +1,3 @@
# Layout Hooks
TODO

3
docs/zh-Hans/UI/AspNetCore/Navigation-Menu.md

@ -0,0 +1,3 @@
# Navigation Menu
TODO

68
docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Buttons.md

@ -1,30 +1,26 @@
# 按钮
ABP框架定义了Tag Helper用于简单的创建bootstrap按钮.
## 介绍
`<abp-button>`
`abp-button` 是创建按钮的主要元素.
## 属性
基本用法:
`<abp-button>` 有7个不同的属性.
````xml
<abp-button button-type="Primary">Click Me</abp-button>
````
* [`button-type`](#button-type)
* [`size`](#size)
* [`busy-text`](#busy-text)
* [`text`](#text)
* [`icon`](#icon)
* [`disabled`](#disabled)
* [`icon-type`](#icon-type)
## Demo
### `button-type`
参阅 [按钮Demo页面](https://bootstrap-taghelpers.abp.io/Components/Buttons) 查看示例.
`button-type` 是一个可选参数. 它的默认值是 `Default`.
## Attributes
`<abp-button button-type="Primary">Button</abp-button>`
### `button-type`
你可以为按钮选择以下按钮类型:
指定按钮的主样式/类型. 应为以下值之一:
* `Default`
* `Default` (default value)
* `Primary`
* `Secondary`
* `Success`
@ -45,11 +41,7 @@ ABP框架定义了Tag Helper用于简单的创建bootstrap按钮.
### `size`
`size` 是一个可选参数. 它的默认值是 `Default`.
`<abp-button size="Default">Button</abp-button>`
你可以为按钮选择以下size类型:
指定按钮的大小. 应为以下值之一:
* `Default`
* `Small`
@ -62,33 +54,33 @@ ABP框架定义了Tag Helper用于简单的创建bootstrap按钮.
### `busy-text`
`busy-text` 是一个字符串类型参数. 当按钮繁忙时设置该文本.
当按钮busy时显示的文本.
### `text`
`text` 是一个字符串类型参数,显示在按钮上.
### `icon`
按钮的文本. 如果你只想为为按钮设置文本,这是一种快捷方式. 例:
`icon` 是一个字符串类型参数. 它的值取决于[`icon-type`](#`icon-type`). 默认情况下,我们对图标使用[Font Awesome](https://fontawesome.com/). 要使用它,你需要将 `icon` 参数设置为图标名称.
````xml
<abp-button button-type="Primary" text="Click Me" />
````
##### 示例
在这个示例中,你可以使用 self-closing 标签将其缩短.
[fa-address-card](https://fontawesome.com/icons/address-card): ![fa-address-card](fa-address-card.png "Address Card")
`<abp-button icon="address-card" text="Address" />`
> 不要忘记: 你不需要写前缀,如果你没有更改 `icon-type` ,它会为[Font Awesome](https://fontawesome.com/)图标自动添加 `fa` 前缀.
### `icon`
### `disabled`
设置按钮的图标. 默认情况下它使用[Font Awesome](https://fontawesome.com/)图标库. 例:
`disabled` 是一个布尔类型参数. 如果你设值为 `true`, 按钮会被禁用.
````xml
<abp-button icon="address-card" text="Address" />
````
### `icon-type`
`icon-type` 是一个可选参数。它的默认值是 `FontAwesome`. 你可以创建自己的图标类型提供程序并更改它.
如果你不想使用font-awesome,你有两个选项:
1. 设置 `icon-type``Other`,并为你的按钮编写图标样式.
2. 如果你不使用图标,请手动使用opening和closing标签,并在标签内写任何代码.
你可以为按钮选择以下图标类型:
### `disabled`
* `FontAwesome`
* `Other`
设置为 `true` 禁用按钮.

27
docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Index.md

@ -1,3 +1,26 @@
## ABP Tag Helpers
# ABP Tag Helpers
"ABP tag helpers" 文档还在创建中. 你现在可以参阅[组件演示](http://bootstrap-taghelpers.abp.io/).
ABP框架定义了一组**标签助手组件**. 简化开发ASP.NET Core (MVC / Razor Pages) 应用程序界面.
## bootstrap 组件包装
大多数标签助手是[Bootstrap](https://getbootstrap.com/) (v4+)的包装. 编写bootstrap代码并不是那么简单,其中包含太多的重复HTML标签并且也没有类型安全. ABP标签助手使其 **简单** 并且 **类型安全**.
我们的目标并不是100%的包装bootstrap组件. 仍然可以编写 **原生bootstrap代码** (实际上标签助手生成的也是原生的bootstrap代码), 但我们建议尽量使用标签助手.
ABP框架还向标准bootstrap组件添加了一些**实用的功能**.
这里是ABP框架包装的组件列表:
* [Buttons](Buttons.md)
* ...
> 在为所有的标签助手完成文档之前,你可以访问 https://bootstrap-taghelpers.abp.io/ 查看在线示例.
## 表单元素
参阅 [demo](https://bootstrap-taghelpers.abp.io/Components/FormElements).
## 动态表单
参阅 [demo](https://bootstrap-taghelpers.abp.io/Components/DynamicForms).

2
docs/zh-Hans/UI/AspNetCore/Theming.md

@ -1,3 +1,3 @@
# Theming
# ASP.NET Core MVC / Razor Pages 主题
TODO

3
docs/zh-Hans/UI/AspNetCore/Toolbars.md

@ -0,0 +1,3 @@
# Toolbars
TODO

16
docs/zh-Hans/UI/AspNetCore/Widgets.md

@ -2,10 +2,10 @@
ABP为创建**可重用的部件**提供了模型和基础设施. 部件系统是[ASP.NET Core ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components)的扩展. 在你有以下需求时,小部件会非常有用;
* 在可复用的 **[模块](../Module-Development-Basics.md)** 中定义部件.
* 在可复用的 **[模块](../../Module-Development-Basics.md)** 中定义部件.
* 在部件中引用 **scripts & styles** 脚本.
* 使用部件创建 **[仪表盘](Dashboards.md)**.
* 支持 **[授权](../Authorization.md)** 与 **[捆绑`bundling`](Bundling-Minification.md)** 的部件
* 使用部件创建 **仪表盘**.
* 支持 **[授权](../../Authorization.md)** 与 **[捆绑`bundling`](Bundling-Minification.md)** 的部件
## 基本部件定义
@ -13,7 +13,7 @@ ABP为创建**可重用的部件**提供了模型和基础设施. 部件系统
第一部,创建一个新的ASP.NET Core View Component:
![widget-basic-files](../images/widget-basic-files.png)
![widget-basic-files](../../images/widget-basic-files.png)
**MySimpleWidgetViewComponent.cs**:
@ -132,7 +132,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
参阅 [本地化文档](../Localization.md) 学习关于本地化资源的更多内容.
参阅 [本地化文档](../../Localization.md) 学习关于本地化资源的更多内容.
## 引用 Style & Script
@ -168,7 +168,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
ABP会考虑到这些依赖关系, 在view/page中使用正确的方法添加部件 . 样式和script可以是物理文件也可以是虚拟文件. 它于[虚拟文件系统](../Virtual-File-System.md)完全集成].
ABP会考虑到这些依赖关系, 在view/page中使用正确的方法添加部件 . 样式和script可以是物理文件也可以是虚拟文件. 它于[虚拟文件系统](../../Virtual-File-System.md)完全集成].
### 定义 Bundle
@ -227,7 +227,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
某些组件可能只对通过身份验证或授权的用户可用,这时可以使用 `Widget` attribute 的以下属性:
* `RequiresAuthentication` (`bool`): 设置为true,只有通过身份验证的用户(登录用户)可用.
* `RequiredPolicies` (`List<string>`): 授权用户的策略名称列表. 有关策略的详细信息请参阅[授权文档](../Authorization.md).
* `RequiredPolicies` (`List<string>`): 授权用户的策略名称列表. 有关策略的详细信息请参阅[授权文档](../../Authorization.md).
示例:
@ -260,7 +260,7 @@ Configure<AbpWidgetOptions>(options =>
});
```
将上面的代码写到[模块](../Module-Development-Basics.md)的 `ConfigureServices` 方法中. `AbpWidgetOptions` 可以完成 `Widget` attribute 的所有功能. 比如为组件添加样式:
将上面的代码写到[模块](../../Module-Development-Basics.md)的 `ConfigureServices` 方法中. `AbpWidgetOptions` 可以完成 `Widget` attribute 的所有功能. 比如为组件添加样式:
````csharp
Configure<AbpWidgetOptions>(options =>

38
docs/zh-Hans/docs-nav.json

@ -42,6 +42,33 @@
}
]
},
{
"text": "指南",
"items": [
{
"text": "自定义应用模块",
"path": "Customizing-Application-Modules-Guide.md",
"items": [
{
"text": "扩展实体",
"path": "Customizing-Application-Modules-Extending-Entities.md"
},
{
"text": "重写服务",
"path": "Customizing-Application-Modules-Overriding-Services.md"
},
{
"text": "重写用户界面",
"path": "Customizing-Application-Modules-Overriding-User-Interface.md"
}
]
},
{
"text": "从ASP.NET Boilerplate迁移",
"path": "AspNet-Boilerplate-Migration-Guide.md"
}
]
},
{
"text": "CLI",
"path": "CLI.md"
@ -249,16 +276,7 @@
},
{
"text": "Tag Helpers",
"items":[
{
"text": "在线演示",
"path": "UI/AspNetCore/Tag-Helpers/Index.md"
},
{
"text": "按钮",
"path": "UI/AspNetCore/Tag-Helpers/Buttons.md"
}
]
"path": "UI/AspNetCore/Tag-Helpers/Index.md"
},
{
"text": "仪表板和小部件(Widget)系统",

BIN
docs/zh-Hans/images/bookstore-added-brand-files.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/zh-Hans/images/bookstore-added-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
docs/zh-Hans/images/bookstore-added-role-js-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/zh-Hans/images/bookstore-brand-area-highlighted.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/zh-Hans/images/bookstore-global-css-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/zh-Hans/images/bookstore-google-analytics-view-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/zh-Hans/images/bookstore-menus-highlighted.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
docs/zh-Hans/images/bookstore-notification-icon-on-toolbar.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
docs/zh-Hans/images/bookstore-notification-view-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/zh-Hans/images/bookstore-toolbar-highlighted.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
docs/zh-Hans/images/docs-module_download-new-abp-project.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 66 KiB

BIN
docs/zh-Hans/images/docs-module_solution-explorer.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/zh-Hans/images/microservice-sample-diagram-3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

BIN
docs/zh-Hans/images/microservice-sample-solution-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/zh-Hans/images/overriding-login-cshtml.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

2
framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj

@ -19,7 +19,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.2" />
</ItemGroup>
</Project>

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelper.cs

@ -1,8 +1,12 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
{
[HtmlTargetElement("abp-modal-footer")]
public class AbpModalFooterTagHelper : AbpTagHelper<AbpModalFooterTagHelper, AbpModalFooterTagHelperService>
{
public AbpModalButtons Buttons { get; set; }
public ButtonsAlign ButtonAlignment { get; set; } = ButtonsAlign.Default;
public AbpModalFooterTagHelper(AbpModalFooterTagHelperService tagHelperService)
: base(tagHelperService)

14
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs

@ -24,6 +24,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
{
output.PostContent.SetHtmlContent(CreateContent());
}
ProcessButtonsAlignment(output);
}
protected virtual string CreateContent()
@ -49,9 +50,18 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
sb.AppendLine("<button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">" + _localizer["Close"] + "</button>");
sb.AppendLine("<button type=\"submit\" class=\"btn btn-primary\" data-busy-text=\"" + _localizer["SavingWithThreeDot"] + "\"><i class=\"fa fa-check\"></i> <span>" + _localizer["Save"] + "</span></button>");
break;
}
}
return sb.ToString();
}
protected virtual void ProcessButtonsAlignment(TagHelperOutput output)
{
if (TagHelper.ButtonAlignment == ButtonsAlign.Default)
{
return;
}
output.Attributes.AddClass("justify-content-" + TagHelper.ButtonAlignment.ToString().ToLowerInvariant());
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalTagHelper.cs

@ -6,6 +6,8 @@
public bool? Centered { get; set; } = false;
public bool? Static { get; set; } = false;
public AbpModalTagHelper(AbpModalTagHelperService tagHelperService)
: base(tagHelperService)
{

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

Loading…
Cancel
Save