Browse Source

Merge branch 'dev' into akin/updated-meta-tags-and-added-localization-keys-of-blogModule

pull/3153/head
Akın Sabri Çam 7 years ago
parent
commit
8a6e81ba61
  1. 4
      docs/en/Authorization.md
  2. 7
      docs/en/Customizing-Application-Modules-Guide.md
  3. 3
      docs/en/IdentityServer-Integration.md
  4. 2
      docs/en/Modules/Blogging.md
  5. 160
      docs/en/Modules/Docs.md
  6. 10
      docs/en/Multi-Tenancy.md
  7. 290
      docs/en/UI/Angular/Config-State.md
  8. 12
      docs/en/UI/AspNetCore/Bundling-Minification.md
  9. 36
      docs/en/UI/AspNetCore/Customization-User-Interface.md
  10. 14
      docs/en/UI/AspNetCore/Widgets.md
  11. 4
      docs/en/docs-nav.json
  12. BIN
      docs/en/images/docs-module_download-new-abp-project.png
  13. BIN
      docs/en/images/docs-module_solution-explorer.png
  14. 140
      docs/zh-Hans/Modules/Docs.md
  15. 6
      docs/zh-Hans/Multi-Tenancy.md
  16. 91
      docs/zh-Hans/Samples/Microservice-Demo.md
  17. 6
      docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md
  18. 3
      docs/zh-Hans/UI/AspNetCore/Layout-Hooks.md
  19. 3
      docs/zh-Hans/UI/AspNetCore/Navigation-Menu.md
  20. 68
      docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Buttons.md
  21. 27
      docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Index.md
  22. 2
      docs/zh-Hans/UI/AspNetCore/Theming.md
  23. 3
      docs/zh-Hans/UI/AspNetCore/Toolbars.md
  24. 16
      docs/zh-Hans/UI/AspNetCore/Widgets.md
  25. BIN
      docs/zh-Hans/images/bookstore-added-brand-files.png
  26. BIN
      docs/zh-Hans/images/bookstore-added-logo.png
  27. BIN
      docs/zh-Hans/images/bookstore-added-role-js-file.png
  28. BIN
      docs/zh-Hans/images/bookstore-brand-area-highlighted.png
  29. BIN
      docs/zh-Hans/images/bookstore-global-css-file.png
  30. BIN
      docs/zh-Hans/images/bookstore-google-analytics-view-component.png
  31. BIN
      docs/zh-Hans/images/bookstore-menus-highlighted.png
  32. BIN
      docs/zh-Hans/images/bookstore-notification-icon-on-toolbar.png
  33. BIN
      docs/zh-Hans/images/bookstore-notification-view-component.png
  34. BIN
      docs/zh-Hans/images/bookstore-toolbar-highlighted.png
  35. BIN
      docs/zh-Hans/images/docs-module_download-new-abp-project.png
  36. BIN
      docs/zh-Hans/images/docs-module_solution-explorer.png
  37. BIN
      docs/zh-Hans/images/microservice-sample-diagram-3.png
  38. BIN
      docs/zh-Hans/images/microservice-sample-solution-2.png
  39. BIN
      docs/zh-Hans/images/overriding-login-cshtml.png
  40. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs
  41. 9
      framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs
  42. 26
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  43. 30
      framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs
  44. 16
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs
  45. 34
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
  46. 20
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
  47. 33
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
  48. 2
      framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj
  49. 8
      framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
  50. 10
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  51. 5
      framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs
  52. 2
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestDataBuilder.cs
  53. 16
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs
  54. 4
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
  55. 2
      modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityRoleAppService.cs
  56. 7
      modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityRoleAppService.cs
  57. 2
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json
  58. 1
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs
  59. 5
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs
  60. 8
      modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs
  61. 4
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs
  62. 4
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs
  63. 12
      modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityRoleAppService_Tests.cs
  64. 17
      modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs
  65. 6
      modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs
  66. 11
      modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs
  67. 4
      modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json
  68. 4
      modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json
  69. 1
      modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs
  70. 6
      modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml
  71. 10
      modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs
  72. 4
      modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs
  73. 3
      npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts
  74. 26
      npm/ng-packs/package.json
  75. 49
      npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts
  76. 26
      npm/ng-packs/packages/core/src/lib/states/config.state.ts
  77. 25
      npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts
  78. 2
      npm/ng-packs/scripts/install-new-dependencies.ts
  79. 1
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj
  80. 6
      templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json
  81. 6
      templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json
  82. 6
      templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json

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.

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

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

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

10
docs/en/Multi-Tenancy.md

@ -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");

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

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"
);
});
});
});
````

36
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#)
@ -158,7 +158,7 @@ If you need, you can also replace [the code behind c# class](https://github.com/
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.
### Overriding Static Resources
## Overriding Static Resources
Overriding a static embedded resource (like JavaScript, Css or image files) of a module is pretty easy. Just place a file in the same path in your solution and let the Virtual File System to handle it.
@ -437,4 +437,34 @@ See the layouts section below to learn more about the layout system.
### Layouts
TODO
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.

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

4
docs/en/docs-nav.json

@ -316,6 +316,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"

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

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/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 =>

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.Mvc.UI.Theme.Basic/BasicTheme.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
{
public const string Name = "Basic";
public string GetLayout(string name, bool fallbackToDefault = true)
public virtual string GetLayout(string name, bool fallbackToDefault = true)
{
switch (name)
{

9
framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs

@ -1,4 +1,5 @@
using System.IO;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Http
{
@ -11,5 +12,13 @@ namespace Microsoft.AspNetCore.Http
return stream.GetAllBytes();
}
}
public static async Task<byte[]> GetAllBytesAsync(this IFormFile file)
{
using (var stream = file.OpenReadStream())
{
return await stream.GetAllBytesAsync();
}
}
}
}

26
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs

@ -43,21 +43,6 @@ namespace Volo.Abp.AspNetCore.Mvc
public override void ConfigureServices(ServiceConfigurationContext context)
{
//Configure Razor
context.Services.Insert(0,
ServiceDescriptor.Singleton<IConfigureOptions<MvcRazorRuntimeCompilationOptions>>(
new ConfigureOptions<MvcRazorRuntimeCompilationOptions>(options =>
{
options.FileProviders.Add(
new RazorViewEngineVirtualFileProvider(
context.Services.GetSingletonInstance<IObjectAccessor<IServiceProvider>>()
)
);
}
)
)
);
Configure<AbpApiDescriptionModelOptions>(options =>
{
options.IgnoredInterfaces.AddIfNotContains(typeof(IAsyncActionFilter));
@ -101,7 +86,16 @@ namespace Volo.Abp.AspNetCore.Mvc
};
})
.AddViewLocalization(); //TODO: How to configure from the application? Also, consider to move to a UI module since APIs does not care about it.
Configure<MvcRazorRuntimeCompilationOptions>(options =>
{
options.FileProviders.Add(
new RazorViewEngineVirtualFileProvider(
context.Services.GetSingletonInstance<IObjectAccessor<IServiceProvider>>()
)
);
});
context.Services.ExecutePreConfiguredActions(mvcBuilder);
//TODO: AddViewLocalization by default..?

30
framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs

@ -1,5 +1,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@ -11,14 +13,34 @@ namespace Volo.Abp.BackgroundWorkers
public abstract class BackgroundWorkerBase : IBackgroundWorker
{
//TODO: Add UOW, Localization and other useful properties..?
public IServiceProvider ServiceProvider { get; set; }
protected readonly object ServiceProviderLock = new object();
public ILogger<BackgroundWorkerBase> Logger { protected get; set; }
protected TService LazyGetRequiredService<TService>(ref TService reference)
=> LazyGetRequiredService(typeof(TService), ref reference);
protected BackgroundWorkerBase()
protected TRef LazyGetRequiredService<TRef>(Type serviceType, ref TRef reference)
{
Logger = NullLogger<BackgroundWorkerBase>.Instance;
if (reference == null)
{
lock (ServiceProviderLock)
{
if (reference == null)
{
reference = (TRef)ServiceProvider.GetRequiredService(serviceType);
}
}
}
return reference;
}
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory;
protected ILogger Logger => _lazyLogger.Value;
private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);
public virtual Task StartAsync(CancellationToken cancellationToken = default)
{
Logger.LogDebug("Started background worker: " + ToString());

16
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs

@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Reflection;
namespace Volo.Abp.Domain.Entities
{
@ -13,12 +11,24 @@ namespace Volo.Abp.Domain.Entities
/// </summary>
public static class EntityHelper
{
public static bool IsEntity([NotNull] Type type)
{
return typeof(IEntity).IsAssignableFrom(type);
}
public static bool IsEntityWithId([NotNull] Type type)
{
foreach (var interfaceType in type.GetInterfaces())
{
if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>))
{
return true;
}
}
return false;
}
public static bool HasDefaultId<TKey>(IEntity<TKey> entity)
{
if (EqualityComparer<TKey>.Default.Equals(entity.Id, default))

34
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs

@ -18,6 +18,34 @@ namespace Volo.Abp.Domain.Repositories
public interface IRepository<TEntity> : IReadOnlyRepository<TEntity>, IBasicRepository<TEntity>
where TEntity : class, IEntity
{
/// <summary>
/// Get a single entity by the given <paramref name="predicate"/>.
/// It returns null if no entity with the given <paramref name="predicate"/>.
/// It throws <see cref="InvalidOperationException"/> if there are multiple entities with the given <paramref name="predicate"/>.
/// </summary>
/// <param name="predicate">A condition to find the entity</param>
/// <param name="includeDetails">Set true to include all children of this entity</param>
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
Task<TEntity> FindAsync(
[NotNull] Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default
);
/// <summary>
/// Get a single entity by the given <paramref name="predicate"/>.
/// It throws <see cref="EntityNotFoundException"/> if there is no entity with the given <paramref name="predicate"/>.
/// It throws <see cref="InvalidOperationException"/> if there are multiple entities with the given <paramref name="predicate"/>.
/// </summary>
/// <param name="predicate">A condition to filter entities</param>
/// <param name="includeDetails">Set true to include all children of this entity</param>
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
Task<TEntity> GetAsync(
[NotNull] Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default
);
/// <summary>
/// Deletes many entities by function.
/// Notice that: All entities fits to given predicate are retrieved and deleted.
@ -30,7 +58,11 @@ namespace Volo.Abp.Domain.Repositories
/// This is useful for ORMs / database APIs those only save changes with an explicit method call, but you need to immediately save changes to the database.
/// </param>
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
Task DeleteAsync([NotNull] Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default);
Task DeleteAsync(
[NotNull] Expression<Func<TEntity, bool>> predicate,
bool autoSave = false,
CancellationToken cancellationToken = default
);
}
public interface IRepository<TEntity, TKey> : IRepository<TEntity>, IReadOnlyRepository<TEntity, TKey>, IBasicRepository<TEntity, TKey>

20
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs

@ -49,6 +49,26 @@ namespace Volo.Abp.Domain.Repositories
protected abstract IQueryable<TEntity> GetQueryable();
public abstract Task<TEntity> FindAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default);
public async Task<TEntity> GetAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
var entity = await FindAsync(predicate, includeDetails, cancellationToken);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity));
}
return entity;
}
public abstract Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default);
protected virtual TQueryable ApplyDataFilters<TQueryable>(TQueryable query)

33
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs

@ -93,6 +93,20 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
return DbSet.AsQueryable();
}
public override async Task<TEntity> FindAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return includeDetails
? await WithDetails()
.Where(predicate)
.SingleOrDefaultAsync(GetCancellationToken(cancellationToken))
: await DbSet
.Where(predicate)
.SingleOrDefaultAsync(GetCancellationToken(cancellationToken));
}
public override async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
var entities = await GetQueryable()
@ -173,18 +187,6 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
}
public virtual TEntity Get(TKey id, bool includeDetails = true)
{
var entity = Find(id, includeDetails);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public virtual async Task<TEntity> GetAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
var entity = await FindAsync(id, includeDetails, GetCancellationToken(cancellationToken));
@ -197,13 +199,6 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
return entity;
}
public virtual TEntity Find(TKey id, bool includeDetails = true)
{
return includeDetails
? WithDetails().FirstOrDefault(e => e.Id.Equals(id))
: DbSet.Find(id);
}
public virtual async Task<TEntity> FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
return includeDetails

2
framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj

@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MailKit" Version="2.4.1" />
<PackageReference Include="MailKit" Version="2.5.2" />
</ItemGroup>
<ItemGroup>

8
framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs

@ -31,6 +31,14 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
return ApplyDataFilters(Collection.AsQueryable());
}
public override Task<TEntity> FindAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return Task.FromResult(Collection.AsQueryable().Where(predicate).SingleOrDefault());
}
public override Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
var entities = Collection.AsQueryable().Where(predicate).ToList();

10
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

@ -169,6 +169,16 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
return GetMongoQueryable();
}
public override async Task<TEntity> FindAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetMongoQueryable()
.Where(predicate)
.SingleOrDefaultAsync(GetCancellationToken(cancellationToken));
}
public virtual IMongoQueryable<TEntity> GetMongoQueryable()
{
return ApplyDataFilters(

5
framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs

@ -246,6 +246,11 @@ namespace Volo.Abp.Domain.Repositories
throw new NotImplementedException();
}
public override Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public override Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();

2
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestDataBuilder.cs

@ -41,7 +41,7 @@ namespace Volo.Abp.TestApp
{
var istanbul = new City(IstanbulCityId, "Istanbul");
istanbul.Districts.Add(new District(istanbul.Id, "Bakirkoy", 1283999));
istanbul.Districts.Add(new District(istanbul.Id, "Mecidiyek�y", 2222321));
istanbul.Districts.Add(new District(istanbul.Id, "Mecidiyekoy", 2222321));
istanbul.Districts.Add(new District(istanbul.Id, "Uskudar", 726172));
await _cityRepository.InsertAsync(new City(Guid.NewGuid(), "Tokyo"));

16
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs

@ -28,6 +28,14 @@ namespace Volo.Abp.TestApp.Testing
person.Phones.Count.ShouldBe(2);
}
[Fact]
public async Task GetAsync_With_Predicate()
{
var person = await PersonRepository.GetAsync(p => p.Name == "Douglas");
person.Name.ShouldBe("Douglas");
person.Phones.Count.ShouldBe(2);
}
[Fact]
public async Task FindAsync_Should_Return_Null_For_Not_Found_Entity()
{
@ -35,6 +43,14 @@ namespace Volo.Abp.TestApp.Testing
person.ShouldBeNull();
}
[Fact]
public async Task FindAsync_Should_Return_Null_For_Not_Found_Entity_With_Predicate()
{
var randomName = Guid.NewGuid().ToString();
var person = await PersonRepository.FindAsync(p => p.Name == randomName);
person.ShouldBeNull();
}
[Fact]
public async Task DeleteAsync()
{

4
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj

@ -32,4 +32,8 @@
<ProjectReference Include="..\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Security.Principal.Windows" Version="4.7.0" />
</ItemGroup>
</Project>

2
modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityRoleAppService.cs

@ -8,6 +8,8 @@ namespace Volo.Abp.Identity
{
public interface IIdentityRoleAppService : IApplicationService
{
Task<ListResultDto<IdentityRoleDto>> GetAllListAsync();
Task<PagedResultDto<IdentityRoleDto>> GetListAsync(PagedAndSortedResultRequestDto input);
Task<IdentityRoleDto> CreateAsync(IdentityRoleCreateDto input);

7
modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityRoleAppService.cs

@ -27,6 +27,13 @@ namespace Volo.Abp.Identity
await _roleManager.GetByIdAsync(id));
}
public virtual async Task<ListResultDto<IdentityRoleDto>> GetAllListAsync()
{
var list = await _roleRepository.GetListAsync();
return new ListResultDto<IdentityRoleDto>(
ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(list));
}
public virtual async Task<PagedResultDto<IdentityRoleDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{
var list = await _roleRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount);

2
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json

@ -82,6 +82,7 @@
"DisplayName:Abp.Identity.Lockout.LockoutDuration": "Lockout duration(seconds)",
"DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "Max failed access attempts",
"DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "Require confirmed email",
"DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Enable phone number confirmation",
"DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Require confirmed phoneNumber",
"DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "Is username update enabled",
"DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "Is email update enabled",
@ -95,6 +96,7 @@
"Description:Abp.Identity.Lockout.LockoutDuration": "The duration a user is locked out for when a lockout occurs.",
"Description:Abp.Identity.Lockout.MaxFailedAccessAttempts": "The number of failed access attempts allowed before a user is locked out, assuming lock out is enabled.",
"Description:Abp.Identity.SignIn.RequireConfirmedEmail": "Whether a confirmed email address is required to sign in.",
"Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Whether the phoneNumber can be confirmed by the user.",
"Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Whether a confirmed telephone number is required to sign in.",
"Description:Abp.Identity.User.IsUserNameUpdateEnabled": "Whether the username can be updated by the user.",
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "Whether the email can be updated by the user."

1
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs

@ -30,6 +30,7 @@
private const string SignInPrefix = Prefix + ".SignIn";
public const string RequireConfirmedEmail = SignInPrefix + ".RequireConfirmedEmail";
public const string EnablePhoneNumberConfirmation = SignInPrefix + ".EnablePhoneNumberConfirmation";
public const string RequireConfirmedPhoneNumber = SignInPrefix + ".RequireConfirmedPhoneNumber";
}

5
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs

@ -72,6 +72,11 @@ namespace Volo.Abp.Identity
false.ToString(), L("DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail"),
L("Description:Abp.Identity.SignIn.RequireConfirmedEmail"),
true),
new SettingDefinition(
IdentitySettingNames.SignIn.EnablePhoneNumberConfirmation,
true.ToString(), L("DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"),
L("Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"),
true),
new SettingDefinition(
IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber,
false.ToString(), L("DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"),

8
modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Application.Dtos;
@ -19,6 +20,13 @@ namespace Volo.Abp.Identity
_roleAppService = roleAppService;
}
[HttpGet]
[Route("all")]
public virtual Task<ListResultDto<IdentityRoleDto>> GetAllListAsync()
{
return _roleAppService.GetAllListAsync();
}
[HttpGet]
public virtual Task<PagedResultDto<IdentityRoleDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{

4
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs

@ -29,9 +29,9 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users
{
UserInfo = new UserInfoViewModel();
var roleDtoList = await _identityRoleAppService.GetListAsync(new PagedAndSortedResultRequestDto());
var roleDtoList = (await _identityRoleAppService.GetAllListAsync()).Items;
Roles = ObjectMapper.Map<IReadOnlyList<IdentityRoleDto>, AssignedRoleViewModel[]>(roleDtoList.Items);
Roles = ObjectMapper.Map<IReadOnlyList<IdentityRoleDto>, AssignedRoleViewModel[]>(roleDtoList);
foreach (var role in Roles)
{

4
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs

@ -31,9 +31,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users
{
UserInfo = ObjectMapper.Map<IdentityUserDto, UserInfoViewModel>(await _identityUserAppService.GetAsync(id));
Roles = ObjectMapper.Map<IReadOnlyList<IdentityRoleDto>, AssignedRoleViewModel[]>(
(await _identityRoleAppService.GetListAsync(new PagedAndSortedResultRequestDto())).Items
);
Roles = ObjectMapper.Map<IReadOnlyList<IdentityRoleDto>, AssignedRoleViewModel[]>((await _identityRoleAppService.GetAllListAsync()).Items);
var userRoleNames = (await _identityUserAppService.GetRolesAsync(UserInfo.Id)).Items.Select(r => r.Name).ToList();
foreach (var role in Roles)

12
modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityRoleAppService_Tests.cs

@ -34,6 +34,18 @@ namespace Volo.Abp.Identity
result.Id.ShouldBe(moderator.Id);
}
[Fact]
public async Task GetAllListAsync()
{
//Act
var result = await _roleAppService.GetAllListAsync();
//Assert
result.Items.Count.ShouldBeGreaterThan(0);
}
[Fact]
public async Task GetListAsync()
{

17
modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs

@ -1,7 +1,22 @@
namespace Volo.Abp.TenantManagement
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Volo.Abp.TenantManagement.Localization;
namespace Volo.Abp.TenantManagement
{
public class TenantCreateDto : TenantCreateOrUpdateDtoBase
{
[Required]
[EmailAddress]
[MaxLength(256)]
public string AdminEmailAddress { get; set; }
[Required]
[MaxLength(128)]
public string AdminPassword { get; set; }
}
}

6
modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs

@ -1,7 +1,11 @@
namespace Volo.Abp.TenantManagement
using System.ComponentModel.DataAnnotations;
namespace Volo.Abp.TenantManagement
{
public abstract class TenantCreateOrUpdateDtoBase
{
[Required]
[StringLength(TenantConsts.MaxNameLength)]
public string Name { get; set; }
}
}

11
modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs

@ -15,7 +15,7 @@ namespace Volo.Abp.TenantManagement
protected ITenantManager TenantManager { get; }
public TenantAppService(
ITenantRepository tenantRepository,
ITenantRepository tenantRepository,
ITenantManager tenantManager,
IDataSeeder dataSeeder)
{
@ -51,10 +51,13 @@ namespace Volo.Abp.TenantManagement
{
//TODO: Handle database creation?
//TODO: Set admin email & password..?
await DataSeeder.SeedAsync(tenant.Id);
await DataSeeder.SeedAsync(
new DataSeedContext(tenant.Id)
.WithProperty("AdminEmail", input.AdminEmailAddress)
.WithProperty("AdminPassword", input.AdminPassword)
);
}
return ObjectMapper.Map<Tenant, TenantDto>(tenant);
}

4
modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json

@ -15,6 +15,8 @@
"Permission:Edit": "Edit",
"Permission:Delete": "Delete",
"Permission:ManageConnectionStrings": "Manage connection strings",
"Permission:ManageFeatures": "Manage features"
"Permission:ManageFeatures": "Manage features",
"DisplayName:AdminEmailAddress": "Admin Email Address",
"DisplayName:AdminPassword": "Admin Password"
}
}

4
modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json

@ -15,6 +15,8 @@
"Permission:Edit": "Düzenleme",
"Permission:Delete": "Silme",
"Permission:ManageConnectionStrings": "Bağlantı cümlelerini yönet",
"Permission:ManageFeatures": "Özellikleri yönet"
"Permission:ManageFeatures": "Özellikleri yönet",
"DisplayName:AdminEmailAddress": "Admin Eposta Adresi",
"DisplayName:AdminPassword": "Admin Şifresi"
}
}

1
modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs

@ -35,6 +35,7 @@ namespace Volo.Abp.TenantManagement
[HttpPost]
public virtual Task<TenantDto> CreateAsync(TenantCreateDto input)
{
ValidateModel();
return _service.CreateAsync(input);
}

6
modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml

@ -12,7 +12,11 @@
<abp-modal>
<abp-modal-header title="@L["NewTenant"]"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Tenant.Name" label="@L["TenantName"].Value" />
<abp-input asp-for="Tenant.Name" />
<abp-input asp-for="Tenant.AdminEmailAddress" />
<abp-input asp-for="Tenant.AdminPassword" />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>

10
modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs

@ -31,8 +31,16 @@ namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants
{
[Required]
[StringLength(TenantConsts.MaxNameLength)]
[Display(Name = "DisplayName:TenantName")]
public string Name { get; set; }
[Required]
[EmailAddress]
[MaxLength(256)]
public string AdminEmailAddress { get; set; }
[Required]
[MaxLength(128)]
public string AdminPassword { get; set; }
}
}
}

4
modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs

@ -59,7 +59,7 @@ namespace Volo.Abp.TenantManagement
public async Task CreateAsync()
{
var tenancyName = Guid.NewGuid().ToString("N").ToLowerInvariant();
var tenant = await _tenantAppService.CreateAsync(new TenantCreateDto { Name = tenancyName });
var tenant = await _tenantAppService.CreateAsync(new TenantCreateDto { Name = tenancyName , AdminEmailAddress = "admin@admin.com", AdminPassword = "123456"});
tenant.Name.ShouldBe(tenancyName);
tenant.Id.ShouldNotBe(default(Guid));
}
@ -69,7 +69,7 @@ namespace Volo.Abp.TenantManagement
{
await Assert.ThrowsAsync<UserFriendlyException>(async () =>
{
await _tenantAppService.CreateAsync(new TenantCreateDto { Name = "acme" });
await _tenantAppService.CreateAsync(new TenantCreateDto { Name = "acme", AdminEmailAddress = "admin@admin.com", AdminPassword = "123456" });
});
}

3
npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts

@ -3,7 +3,6 @@ import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgModule } from '@angular/core';
import { ThemeBasicModule } from '@abp/ng.theme.basic';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { TableModule } from 'primeng/table';
import { NgxValidateCoreModule } from '@ngx-validate/core';
@NgModule({
@ -12,7 +11,6 @@ import { NgxValidateCoreModule } from '@ngx-validate/core';
CoreModule,
ThemeSharedModule,
ThemeBasicModule,
TableModule,
NgbDropdownModule,
NgxValidateCoreModule,
],
@ -20,7 +18,6 @@ import { NgxValidateCoreModule } from '@ngx-validate/core';
CoreModule,
ThemeSharedModule,
ThemeBasicModule,
TableModule,
NgbDropdownModule,
NgxValidateCoreModule,
],

26
npm/ng-packs/package.json

@ -21,19 +21,19 @@
"generate:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
},
"devDependencies": {
"@abp/ng.account": "~2.1.0",
"@abp/ng.account.config": "~2.1.0",
"@abp/ng.core": "~2.1.0",
"@abp/ng.feature-management": "~2.1.0",
"@abp/ng.identity": "~2.1.0",
"@abp/ng.identity.config": "~2.1.0",
"@abp/ng.permission-management": "~2.1.0",
"@abp/ng.setting-management": "~2.1.0",
"@abp/ng.setting-management.config": "~2.1.0",
"@abp/ng.tenant-management": "~2.1.0",
"@abp/ng.tenant-management.config": "~2.1.0",
"@abp/ng.theme.basic": "~2.1.0",
"@abp/ng.theme.shared": "~2.1.0",
"@abp/ng.account": "~2.2.0",
"@abp/ng.account.config": "~2.2.0",
"@abp/ng.core": "~2.2.0",
"@abp/ng.feature-management": "~2.2.0",
"@abp/ng.identity": "~2.2.0",
"@abp/ng.identity.config": "~2.2.0",
"@abp/ng.permission-management": "~2.2.0",
"@abp/ng.setting-management": "~2.2.0",
"@abp/ng.setting-management.config": "~2.2.0",
"@abp/ng.tenant-management": "~2.2.0",
"@abp/ng.tenant-management.config": "~2.2.0",
"@abp/ng.theme.basic": "~2.2.0",
"@abp/ng.theme.shared": "~2.2.0",
"@angular-builders/jest": "^8.2.0",
"@angular-devkit/build-angular": "~0.803.21",
"@angular-devkit/build-ng-packagr": "~0.803.21",

49
npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts

@ -1,9 +1,9 @@
import { ABP, SetTenant, SessionState } from '@abp/ng.core';
import { ABP, SetTenant, SessionState, GetAppConfiguration } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngxs/store';
import { throwError } from 'rxjs';
import { catchError, take, finalize } from 'rxjs/operators';
import { catchError, take, finalize, switchMap } from 'rxjs/operators';
import snq from 'snq';
import { AccountService } from '../../services/account.service';
import { Account } from '../../models/account';
@ -52,29 +52,32 @@ export class TenantBoxComponent
);
return throwError(err);
}),
switchMap(({ success, tenantId }) => {
if (success) {
this.tenant = {
id: tenantId,
name: this.tenant.name,
};
this.tenantName = this.tenant.name;
this.isModalVisible = false;
} else {
this.toasterService.error(
'AbpUiMultiTenancy::GivenTenantIsNotAvailable',
'AbpUi::Error',
{
messageLocalizationParams: [this.tenant.name],
},
);
this.tenant = {} as ABP.BasicItem;
this.tenantName = '';
}
this.store.dispatch(new SetTenant(success ? this.tenant : null));
return this.store.dispatch(new GetAppConfiguration());
}),
)
.subscribe(({ success, tenantId }) => {
if (success) {
this.tenant = {
id: tenantId,
name: this.tenant.name,
};
this.tenantName = this.tenant.name;
this.isModalVisible = false;
} else {
this.toasterService.error(
'AbpUiMultiTenancy::GivenTenantIsNotAvailable',
'AbpUi::Error',
{
messageLocalizationParams: [this.tenant.name],
},
);
this.tenant = {} as ABP.BasicItem;
}
this.store.dispatch(new SetTenant(success ? this.tenant : null));
});
.subscribe();
} else {
this.store.dispatch(new SetTenant(null));
this.store.dispatch([new SetTenant(null), new GetAppConfiguration()]);
this.tenantName = null;
this.isModalVisible = false;
}

26
npm/ng-packs/packages/core/src/lib/states/config.state.ts

@ -1,11 +1,12 @@
import { Injectable } from '@angular/core';
import { Action, createSelector, Selector, State, StateContext, Store } from '@ngxs/store';
import { of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import snq from 'snq';
import {
AddRoute,
GetAppConfiguration,
PatchRouteByName,
AddRoute,
SetEnvironment,
} from '../actions/config.actions';
import { SetLanguage } from '../actions/session.actions';
@ -14,7 +15,6 @@ import { Config } from '../models/config';
import { ApplicationConfigurationService } from '../services/application-configuration.service';
import { organizeRoutes } from '../utils/route-utils';
import { SessionState } from './session.state';
import { Injectable } from '@angular/core';
@State<Config.State>({
name: 'ConfigState',
@ -91,24 +91,24 @@ export class ConfigState {
const selector = createSelector([ConfigState], (state: Config.State) => {
return snq(() => state.setting.values[key]);
});
return selector;
}
static getSettings(keyword?: string) {
const selector = createSelector([ConfigState], (state: Config.State) => {
if (keyword) {
const keys = snq(
() => Object.keys(state.setting.values).filter(key => key.indexOf(keyword) > -1),
[],
);
if (keys.length) {
return keys.reduce((acc, key) => ({ ...acc, [key]: state.setting.values[key] }), {});
}
}
const settings = snq(() => state.setting.values, {});
if (!keyword) return settings;
return snq(() => state.setting.values, {});
const keysFound = Object.keys(settings).filter(key => key.indexOf(keyword) > -1);
return keysFound.reduce((acc, key) => {
acc[key] = settings[key];
return acc;
}, {});
});
return selector;
}

25
npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts

@ -1,12 +1,12 @@
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest';
import { Store } from '@ngxs/store';
import { ReplaySubject, timer, Subject, of } from 'rxjs';
import clone from 'just-clone';
import { of, ReplaySubject, timer } from 'rxjs';
import { AddRoute, PatchRouteByName, SetLanguage } from '../actions';
import { ABP } from '../models';
import { Config } from '../models/config';
import { ApplicationConfigurationService, ConfigStateService } from '../services';
import { ConfigState } from '../states';
import { SetLanguage, PatchRouteByName, AddRoute } from '../actions';
import clone from 'just-clone';
import { ABP } from '../models';
export const CONFIG_STATE_DATA = {
environment: {
@ -116,6 +116,7 @@ export const CONFIG_STATE_DATA = {
},
setting: {
values: {
'Abp.Custom.SomeSetting': 'X',
'Abp.Localization.DefaultLanguage': 'en',
},
},
@ -218,14 +219,14 @@ describe('ConfigState', () => {
});
describe('#getSettings', () => {
it('should return settings', () => {
expect(ConfigState.getSettings('Localization')(CONFIG_STATE_DATA)).toEqual({
'Abp.Localization.DefaultLanguage': 'en',
});
expect(ConfigState.getSettings('AllSettings')(CONFIG_STATE_DATA)).toEqual(
CONFIG_STATE_DATA.setting.values,
);
test.each`
keyword | expected
${undefined} | ${CONFIG_STATE_DATA.setting.values}
${'Localization'} | ${{ 'Abp.Localization.DefaultLanguage': 'en' }}
${'X'} | ${{}}
${'localization'} | ${{}}
`('should return $expected when keyword is given as $keyword', ({ keyword, expected }) => {
expect(ConfigState.getSettings(keyword)(CONFIG_STATE_DATA)).toEqual(expected);
});
});

2
npm/ng-packs/scripts/install-new-dependencies.ts

@ -17,7 +17,7 @@ const updateAndInstall = async () => {
...packageJson.devDependencies,
...dependencies,
...peerDependencies,
...{ [name]: `^${version}` },
...{ [name]: `~${version}` },
};
packageJson.devDependencies = Object.keys(packageJson.devDependencies)

1
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj

@ -16,6 +16,7 @@
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy\Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" />

6
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json

@ -0,0 +1,6 @@
{
"culture": "sl",
"texts": {
"ManageYourProfile": "Upravljajte svojim profilom"
}
}

6
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json

@ -0,0 +1,6 @@
{
"culture": "zh-Hans",
"texts": {
"ManageYourProfile": "管理个人资料"
}
}

6
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json

@ -0,0 +1,6 @@
{
"culture": "zh-Hant",
"texts": {
"ManageYourProfile": "管理個人資料"
}
}
Loading…
Cancel
Save