Browse Source

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

pull/3061/head
liangshiwei 7 years ago
parent
commit
b0ca254d9e
  1. 1
      .gitignore
  2. 10
      docs/cs/AspNetCore/Widgets.md
  3. 3
      docs/cs/CLI.md
  4. 2
      docs/cs/Getting-Started-Angular-Template.md
  5. 2
      docs/cs/Getting-Started-AspNetCore-MVC-Template.md
  6. 4
      docs/en/AspNetCore/Client-Side-Package-Management.md
  7. 6
      docs/en/AspNetCore/Dynamic-CSharp-API-Clients.md
  8. 4
      docs/en/AspNetCore/Tag-Helpers/Buttons.md
  9. 2
      docs/en/AspNetCore/Tag-Helpers/Index.md
  10. 10
      docs/en/AspNetCore/Widgets.md
  11. 6
      docs/en/Background-Workers-Quartz.md
  12. 0
      docs/en/Background-Workers.md
  13. 1
      docs/en/CLI.md
  14. 90
      docs/en/Customizing-Application-Modules-Guide.md
  15. 95
      docs/en/Entities.md
  16. 2
      docs/en/Getting-Started-Angular-Template.md
  17. 2
      docs/en/Getting-Started-AspNetCore-MVC-Template.md
  18. 4
      docs/en/Localization.md
  19. 11
      docs/en/Module-Development-Basics.md
  20. 4
      docs/en/Multi-Tenancy.md
  21. 95
      docs/en/Samples/Microservice-Demo.md
  22. 27
      docs/en/docs-nav.json
  23. BIN
      docs/en/images/microservice-sample-diagram-3.png
  24. BIN
      docs/en/images/microservice-sample-solution-2.png
  25. 4
      docs/pt-BR/Getting-Started-Angular-Template.md
  26. 2
      docs/pt-BR/Getting-Started-AspNetCore-MVC-Template.md
  27. 4
      docs/pt-BR/Localization.md
  28. 4
      docs/zh-Hans/AspNetCore/Client-Side-Package-Management.md
  29. 6
      docs/zh-Hans/AspNetCore/Dynamic-CSharp-API-Clients.md
  30. 10
      docs/zh-Hans/AspNetCore/Widgets.md
  31. 3
      docs/zh-Hans/CLI.md
  32. 4
      docs/zh-Hans/Exception-Handling.md
  33. 2
      docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md
  34. 2
      docs/zh-Hans/Localization.md
  35. 2
      docs/zh-Hans/Module-Development-Basics.md
  36. 4
      docs/zh-Hans/Multi-Tenancy.md
  37. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSize.cs
  38. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSizeExtensions.cs
  39. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NugetPackageToLocalReferenceConverter.cs
  40. 5
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAuthorizationHandler.cs
  41. 5
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAuthorizationHandler.cs
  42. 3
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json
  43. 3
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/tr.json
  44. 2
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/CommentsController.cs
  45. 22
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml
  46. 3
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml.cs
  47. 32
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/detail.js
  48. 3
      modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj
  49. 6
      modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs
  50. 5
      modules/docs/app/VoloDocs.Web/appsettings.json
  51. 2
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/IDocumentAdminAppService.cs
  52. 22
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs
  53. 7
      modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs
  54. 19
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentSearchInput.cs
  55. 24
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentSearchOutput.cs
  56. 5
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/IDocumentAppService.cs
  57. 32
      modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs
  58. 7
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/DocsDomainErrorCodes.cs
  59. 1
      modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
  60. 16
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs
  61. 27
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/DefaultElasticClientProvider.cs
  62. 15
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/DocsElasticSearchOptions.cs
  63. 49
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/DocumentChangedEventHandler.cs
  64. 189
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs
  65. 29
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/EsDocument.cs
  66. 20
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IDocumentFullSearch.cs
  67. 9
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IElasticClientProvider.cs
  68. 2
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json
  69. 2
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json
  70. 1
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json
  71. 17
      modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Documents/DocsDocumentController.cs
  72. 3
      modules/docs/src/Volo.Docs.Web/DocsWebModule.cs
  73. 18
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml
  74. 3
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs
  75. 10
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js
  76. 67
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Search.cshtml
  77. 73
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Search.cshtml.cs
  78. 12
      modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantEto.cs
  79. 2
      modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainMappingProfile.cs
  80. 7
      modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs
  81. 2
      npm/package.json
  82. 13
      npm/preview-publish.ps1
  83. 20
      samples/MicroserviceDemo/MicroserviceDemo.sln
  84. 6
      samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj
  85. 8
      samples/MicroserviceDemo/applications/AuthServer.Host/AuthServerDataSeeder.cs
  86. 21
      samples/MicroserviceDemo/applications/AuthServer.Host/AuthServerHostModule.cs
  87. 4
      samples/MicroserviceDemo/applications/AuthServer.Host/EntityFrameworkCore/AuthServerDbContext.cs
  88. 1675
      samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20200304125631_Added_Tenant_Management.Designer.cs
  89. 65
      samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20200304125631_Added_Tenant_Management.cs
  90. 1706
      samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20200304155131_Added_Feature_Management.Designer.cs
  91. 37
      samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20200304155131_Added_Feature_Management.cs
  92. 116
      samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/AuthServerDbContextModelSnapshot.cs
  93. 1
      samples/MicroserviceDemo/applications/AuthServer.Host/Program.cs
  94. 6
      samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminApp.Host.csproj
  95. 28
      samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminAppHostModule.cs
  96. 43
      samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminAppMenuContributor.cs
  97. 9
      samples/MicroserviceDemo/applications/BackendAdminApp.Host/Controllers/AccountController.cs
  98. 1
      samples/MicroserviceDemo/applications/BackendAdminApp.Host/Program.cs
  99. 36
      samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs
  100. 1
      samples/MicroserviceDemo/applications/ConsoleClientDemo/ConsoleClientDemo.csproj

1
.gitignore

@ -292,3 +292,4 @@ samples/MicroserviceDemo/microservices/BloggingService.Host/Logs/logs.txt
samples/MicroserviceDemo/applications/ConsoleClientDemo/Logs/logs.txt
modules/docs/app/Volo.DocsTestApp/Logs/logs.txt
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Logs/logs.txt
samples/MicroserviceDemo/microservices/TenantManagementService.Host/Logs/logs.txt

10
docs/cs/AspNetCore/Widgets.md

@ -478,19 +478,19 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
## WidgetOptions
Jako alternativu k atributu `Widget` můžete ke konfiguraci widgetů použít `WidgetOptions`:
Jako alternativu k atributu `Widget` můžete ke konfiguraci widgetů použít `AbpWidgetOptions`:
```csharp
Configure<WidgetOptions>(options =>
Configure<AbpWidgetOptions>(options =>
{
options.Widgets.Add<MySimpleWidgetViewComponent>();
});
```
Toto vepište do metody `ConfigureServices` vašeho [modulu](../Module-Development-Basics.md). Veškerá konfigurace udělaná přes atribut `Widget` je dostupná i za pomoci `WidgetOptions`. Příklad konfigurace, která přidává styl pro widget:
Toto vepište do metody `ConfigureServices` vašeho [modulu](../Module-Development-Basics.md). Veškerá konfigurace udělaná přes atribut `Widget` je dostupná i za pomoci `AbpWidgetOptions`. Příklad konfigurace, která přidává styl pro widget:
````csharp
Configure<WidgetOptions>(options =>
Configure<AbpWidgetOptions>(options =>
{
options.Widgets
.Add<MySimpleWidgetViewComponent>()
@ -498,7 +498,7 @@ Configure<WidgetOptions>(options =>
});
````
> Tip: `WidgetOptions` lze také použít k získání existujícího widgetu a ke změně jeho konfigurace. To je obzvláště užitečné, pokud chcete změnit konfiguraci widgetu uvnitř modulu používaného vaší aplikací. Použíjte `options.Widgets.Find` k získání existujícího `WidgetDefinition`.
> Tip: `AbpWidgetOptions` lze také použít k získání existujícího widgetu a ke změně jeho konfigurace. To je obzvláště užitečné, pokud chcete změnit konfiguraci widgetu uvnitř modulu používaného vaší aplikací. Použíjte `options.Widgets.Find` k získání existujícího `WidgetDefinition`.
## Podívejte se také na

3
docs/cs/CLI.md

@ -1,4 +1,4 @@
# ABP CLI
# ABP CLI
ABP CLI (Command Line Interface) je nástroj v příkazovém řádku k provádění některých běžných úkonů v řešeních založených na ABP.
@ -109,6 +109,7 @@ abp add-module Volo.Blogging
* `--solution` nebo `-s`: Určuje cestu k řešení (.sln). Pokud není zadáno, CLI se pokusí najít soubor .sln v aktuálním adresáři.
* `--skip-db-migrations`: Pro poskytovatele databáze EF Core automaticky přidá nový kód první migrace (`Add-Migration`) a v případě potřeby aktualizuje databázi (`Update-Database`). Tuto možnost určete k vynechání této operace.
* `-sp` nebo `--startup-project`: Relativní cesta ke složce spouštěcího projektu. Výchozí hodnota je aktuální adresář.
* `--with-source-code`: Místo balíčků NuGet/NPM přidejte zdrojový kód modulu.
### update

2
docs/cs/Getting-Started-Angular-Template.md

@ -29,7 +29,7 @@ Vytvořené řešení vyžaduje;
* [Visual Studio 2019 (v16.4.0+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://yarnpkg.com/)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### Struktura řešení

2
docs/cs/Getting-Started-AspNetCore-MVC-Template.md

@ -29,7 +29,7 @@ Vytvořené řešení vyžaduje;
* [Visual Studio 2019 (v16.4.0+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://yarnpkg.com/)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### Struktura řešení

4
docs/en/AspNetCore/Client-Side-Package-Management.md

@ -5,7 +5,7 @@ ABP framework can work with any type of client side package management systems.
However, ABP framework works best with **NPM/Yarn**. By default, built-in modules are configured to work with NPM/Yarn.
Finally, we suggest the [**Yarn**](https://yarnpkg.com/) over the NPM since it's faster, stable and also compatible with the NPM.
Finally, we suggest the [**Yarn**](https://classic.yarnpkg.com/) over the NPM since it's faster, stable and also compatible with the NPM.
### @ABP NPM Packages
@ -41,7 +41,7 @@ After depending on a NPM package, all you should do is to run the **yarn** comma
yarn
````
Alternatively, you can use `npm install` but [Yarn](https://yarnpkg.com/) is suggested as mentioned before.
Alternatively, you can use `npm install` but [Yarn](https://classic.yarnpkg.com/) is suggested as mentioned before.
#### Package Contribution

6
docs/en/AspNetCore/Dynamic-CSharp-API-Clients.md

@ -106,12 +106,12 @@ While you can inject `IBookAppService` like above to use the client proxy, you c
### RemoteServiceOptions
`RemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example:
`AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<RemoteServiceOptions>(options =>
context.Services.Configure<AbpRemoteServiceOptions>(options =>
{
options.RemoteServices.Default =
new RemoteServiceConfiguration("http://localhost:53929/");
@ -147,7 +147,7 @@ context.Services.AddHttpClientProxies(
);
````
`remoteServiceName` parameter matches the service endpoint configured via `RemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint.
`remoteServiceName` parameter matches the service endpoint configured via `AbpRemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint.
### As Default Services

4
docs/en/AspNetCore/Tag-Helpers/Buttons.md

@ -63,7 +63,7 @@ You can choose one of the size type listed below.
### `busy-text`
`busy-text` is a string parameter. IT shows the text while the button is busy.
`busy-text` is a string parameter. It shows the text while the button is busy.
### `text`
@ -92,4 +92,4 @@ You can choose one of the size type listed below.
You can choose one of the size type listed below.
* `FontAwesome`
* `Other`
* `Other`

2
docs/en/AspNetCore/Tag-Helpers/Index.md

@ -1,3 +1,3 @@
## ABP Tag Helpers
"ABP tag helpers" is not documented yet. You can see a [demo of components](http://bootstrap-taghelpers.abp.io/) for now.
"ABP tag helpers" documentation is creating now. You can see a [demo of components](http://bootstrap-taghelpers.abp.io/) for now.

10
docs/en/AspNetCore/Widgets.md

@ -478,19 +478,19 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
## WidgetOptions
As alternative to the `Widget` attribute, you can use the `WidgetOptions` to configure widgets:
As alternative to the `Widget` attribute, you can use the `AbpWidgetOptions` to configure widgets:
```csharp
Configure<WidgetOptions>(options =>
Configure<AbpWidgetOptions>(options =>
{
options.Widgets.Add<MySimpleWidgetViewComponent>();
});
```
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 `WidgetOptions`. 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<WidgetOptions>(options =>
Configure<AbpWidgetOptions>(options =>
{
options.Widgets
.Add<MySimpleWidgetViewComponent>()
@ -498,7 +498,7 @@ Configure<WidgetOptions>(options =>
});
````
> Tip: `WidgetOptions` can also be used to get an existing widget and change its configuration. This is especially useful if you want to modify the configuration of a widget inside a module used by your application. Use `options.Widgets.Find` to get an existing `WidgetDefinition`.
> Tip: `AbpWidgetOptions` can also be used to get an existing widget and change its configuration. This is especially useful if you want to modify the configuration of a widget inside a module used by your application. Use `options.Widgets.Find` to get an existing `WidgetDefinition`.
## See Also

6
docs/en/Background-Workers-Quartz.md

@ -38,7 +38,7 @@ public class YourModule : AbpModule
### Configuration
See [Configuration](Background-Jobs-Quartz.md#Configuration).
See [Configuration](Background-Jobs-Quartz#Configuration).
### Create a Background Worker
@ -61,8 +61,8 @@ public class MyLogWorker : QuartzBackgroundWorkerBase
}
````
We simply implemented the Execute method to write a log. The background worker is a **singleton by default**. If you want, you can also implement a [dependency interface](Dependency-Injection.md#DependencyInterfaces) to register it as another life cycle.
We simply implemented the Execute method to write a log. The background worker is a **singleton by default**. If you want, you can also implement a [dependency interface](Dependency-Injection#DependencyInterfaces) to register it as another life cycle.
### More
Please see Quartz's [documentation](https://www.quartz-scheduler.net/documentation/index.html) for more information.
Please see Quartz's [documentation](https://www.quartz-scheduler.net/documentation/index.html) for more information.

0
docs/en/Background-Worker.md → docs/en/Background-Workers.md

1
docs/en/CLI.md

@ -109,6 +109,7 @@ abp add-module Volo.Blogging
* `--solution` or `-s`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
* `--skip-db-migrations`: For EF Core database provider, it automatically adds a new code first migration (`Add-Migration`) and updates the database (`Update-Database`) if necessary. Specify this option to skip this operation.
* `-sp` or `--startup-project`: Relative path to the project folder of the startup project. Default value is the current folder.
* `--with-source-code`: Add source code of the module instead of NuGet/NPM packages.
### update

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

@ -0,0 +1,90 @@
# Customizing the Existing Modules
ABP Framework provides was designed to support to build fully [modular applications](Module-Development-Basics.md) and systems. It also provides some [pre-built application modules](Modules/Index.md) those are **ready to use** in any kind of application.
For example, you can **re-use** the [Identity Management Module](Modules/Identity.md) to add user, role and permission management to your application. The [application startup template](Startup-Templates/Application.md) already comes with Identity and some other modules **pre-installed**.
## Re-Using an Application Module
You have two options to re-use an application module.
### As Package References
You can add **NuGet** & **NPM** package references of the related module to your application and configure the module (based on its documentation) to integrate to your application.
As mentioned before, the [application startup template](Startup-Templates/Application.md) already comes with some **fundamental modules pre-installed**. It uses the modules as NuGet & NPM package references.
This approach has the following benefits:
* Your solution will be **clean** and only contains your **own application code**.
* You can **easily upgrade** a module when a new version is available. `abp update` [CLI](CLI.md) command makes it even easier. In this way, you can continue to get **new features and bug fixes**.
However, there is a drawback:
* You may not able to **customize** the module source code as it is in your own solution.
This document explains **how to customize or extend** a depended module without need to change its source code. While it is limited compared to a full source code change opportunity, there are still some good ways to make some customizations.
If you don't think to make huge changes on the pre-built modules, re-using them as package reference is the recommended way.
### Including the Source Code
If you want to make **huge changes** or add **major features** on a pre-built module, but the available extension points are not enough, you can consider to directly work the source code of the depended module.
In this case, you typically **add the source code** of the module to your solution and **replace package references** by local project references. **[ABP CLI](CLI.md)** automates this process for you.
#### Separating the Module Solution
You may prefer to not include the module source code **directly into your solution**. Every module consists of 10+ project files and adding **multiple modules** may impact on the **size** of your solution **load & development time.** Also, you may have different development teams working on different modules, so you don't want to make the module code available to the application development team.
In any case, you can create a **separate solution** for the desired module and depend on the module as project references out of the solution. We do it like that for the [abp repository](https://github.com/abpframework/abp/).
> One problem we see is Visual Studio doesn't play nice with this kind of approach (it doesn't support well to have references to local projects out of the solution directory). If you get error while building the application (depends on an external module), run `dotnet restore` in the command line after opening the application's solution in the Visual Studio.
#### Publishing the Customized Module as Packages
One alternative scenario could be re-packaging the module source code (as NuGet/NPM packages) and using as package references. You can use a local private NuGet/NPM server for your company.
## Module Customization / Extending Approaches
This section suggests some approaches if you decided to use pre-built application modules as NuGet/NPM package references.
### Extending Entities
In some cases, you may want to add some additional properties (and database fields) for an entity defined in a depended module. This section will cover some different approaches to make this possible.
#### Extra Properties
[Extra properties](Entities.md) is a way of storing some additional data on an entity without changing it. The entity should implement the `IHasExtraProperties` interface to allow it. All the aggregate root entities defined in the pre-built modules implement the `IHasExtraProperties` interface, so you can store extra properties on these entities.
Example:
````csharp
//SET AN EXTRA PROPERTY
var user = await _identityUserRepository.GetAsync(userId);
user.SetProperty("Title", "My custom title value!");
await _identityUserRepository.UpdateAsync(user);
//GET AN EXTRA PROPERTY
var user = await _identityUserRepository.GetAsync(userId);
return user.GetProperty<string>("Title");
````
This approach is very easy to use and available out of the box. No extra code needed. You can store more than one property at the same time by using different property names (like `Title` here).
Extra properties are stored as a single `JSON` formatted string value in the database for the EF Core. For MongoDB, they are stored as separate fields of the document.
See the [entities document](Entities.md) for more about the extra properties system.
> It is possible to perform a **business logic** based on the value of an extra property. You can **override** a service method and get or set the value as shown above. Overriding services will be discussed below.
#### Creating a New Entity Maps to the Same Database Table/Collection
While using the extra properties approach is easy to use and suitable for some scenarios, it has some drawbacks described in the [entities document](Entities.md).
Another approach can be creating your own entity mapped to the same database table (or collection for a MongoDB database).
`AppUser` entity in the [application startup template](Startup-Templates/Application.md) already implements this approach. [EF Core Migrations document](Entity-Framework-Core-Migrations.md) describes how to implement it and manage database migrations in such a case. It is also possible for MongoDB, while this time you won't deal with the database migration problems.
#### Creating a New Entity with Its Own Database Table/Collection

95
docs/en/Entities.md

@ -295,6 +295,99 @@ All these base classes also have non-generic versions to take `AuditedEntity` an
All these base classes also have `...WithUser` pairs, like `FullAuditedAggregateRootWithUser<TUser>` and`FullAuditedAggregateRootWithUser<TKey, TUser>`. This makes possible to add a navigation property to your user entity. However, it is not a good practice to add navigation properties between aggregate roots, so this usage is not suggested (unless you are using an ORM, like EF Core, that well supports this scenario and you really need it - otherwise remember that this approach doesn't work for NoSQL databases like MongoDB where you must truly implement the aggregate pattern).
## Extra Properties
ABP defines the `IHasExtraProperties` interface that can be implemented by an entity to be able to dynamically set and get properties for the entity. `AggregateRoot` base class already implements the `IHasExtraProperties` interface. If you've derived from this class (or one of the related audit class defined above), you can directly use the API.
### GetProperty & SetProperty Extension Methods
These extension methods are the recommended way to get and set data for an entity. Example:
````csharp
public class ExtraPropertiesDemoService : ITransientDependency
{
private readonly IIdentityUserRepository _identityUserRepository;
public ExtraPropertiesDemoService(IIdentityUserRepository identityUserRepository)
{
_identityUserRepository = identityUserRepository;
}
public async Task SetTitle(Guid userId, string title)
{
var user = await _identityUserRepository.GetAsync(userId);
//SET A PROPERTY
user.SetProperty("Title", title);
await _identityUserRepository.UpdateAsync(user);
}
public async Task<string> GetTitle(Guid userId)
{
var user = await _identityUserRepository.GetAsync(userId);
//GET A PROPERTY
return user.GetProperty<string>("Title");
}
}
````
* Property's **value is object** and can be any type of object (string, int, bool... etc).
* `GetProperty` returns `null` if given property was not set before.
* You can store more than one property at the same time by using different **property names** (like `Title` here).
It would be a good practice to **define a constant** for the property name to prevent typo errors. It would be even a better practice to **define extension methods** to take the advantage of the intellisense. Example:
````csharp
public static class IdentityUserExtensions
{
private const string TitlePropertyName = "Title";
public static void SetTitle(this IdentityUser user, string title)
{
user.SetProperty(TitlePropertyName, title);
}
public static string GetTitle(this IdentityUser user)
{
return user.GetProperty<string>(TitlePropertyName);
}
}
````
Then you can directly use `user.SetTitle("...")` and `user.GetTitle()` for an `IdentityUser` object.
### HasProperty & RemoveProperty Extension Methods
* `HasProperty` is used to check if the object has a property set before.
* `RemoveProperty` is used to remove a property from the object. You can use this instead of setting a `null` value.
### How it is Implemented?
`IHasExtraProperties` interface requires to define a `Dictionary<string, object>` property, named `ExtraProperties`, for the implemented class.
So, you can directly use the `ExtraProperties` property to use the dictionary API, if you like. However, `SetProperty` and `GetProperty` methods are the recommended ways since they also check for `null`s.
#### How is it Stored?
The way to store this dictionary in the database depends on the database provider you're using.
* For [Entity Framework Core](Entity-Framework-Core.md), it is stored in a single `ExtraProperties` field as a `JSON` string. Serializing to `JSON` and deserializing from the `JSON` are automatically done by the ABP Framework using the [value conversions](https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions) system of the EF Core.
* For [MongoDB](MongoDB.md), it is stored as a **regular field**, since MongoDB naturally supports this kind of [extra elements](https://mongodb.github.io/mongo-csharp-driver/1.11/serialization/#supporting-extra-elements) system.
### Discussion for the Extra Properties
Extra Properties system is especially useful if you are using a **re-usable module** that defines an entity inside and you want to get/set some data related to this entity in an easy way. You normally **don't need** to this system for your own entities, because it has the following drawbacks:
* It is **not fully type safe**.
* It is **not easy to [auto map](Object-To-Object-Mapping.md)** these properties from/to other objects.
* It **doesn't create fields** in the database table for EF Core, so it will not be easy to create indexes or search/order by this field in the database side.
### Extra Properties Behind Entities
`IHasExtraProperties` is not restricted to be used with entities. You can implement this interface for any kind of class and use the `GetProperty`, `SetProperty` and other related methods.
## See Also
* [Best practice guide to design the entities](Best-Practices/Entities.md)
* [Best practice guide to design the entities](Best-Practices/Entities.md)

2
docs/en/Getting-Started-Angular-Template.md

@ -29,7 +29,7 @@ The created solution requires;
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://yarnpkg.com/)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### The Solution Structure

2
docs/en/Getting-Started-AspNetCore-MVC-Template.md

@ -29,7 +29,7 @@ The created solution requires;
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://yarnpkg.com/)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### The Solution Structure

4
docs/en/Localization.md

@ -46,7 +46,7 @@ public class MyModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>
Configure<AbpVirtualFileSystemOptions>(options =>
{
// "YourRootNameSpace" is the root namespace of your project. It can be empty if your root namespace is empty.
options.FileSets.AddEmbedded<MyModule>("YourRootNameSpace");
@ -67,7 +67,7 @@ In this example;
* Added a new localization resource with "en" (English) as the default culture.
* Used JSON files to store the localization strings.
* JSON files are embedded into the assembly using `VirtualFileSystemOptions` (see [virtual file system](Virtual-File-System.md)).
* JSON files are embedded into the assembly using `AbpVirtualFileSystemOptions` (see [virtual file system](Virtual-File-System.md)).
JSON files are located under "/Localization/Resources/Test" project folder as shown below:

11
docs/en/Module-Development-Basics.md

@ -2,7 +2,14 @@
## Introduction
ABP is a **modular application framework** which consists of dozens of **nuget packages**. It also provides a complete infrastructure to build your own application modules which may have entities, services, database integration, APIs, UI components and so on.
ABP Framework was designed to support to build fully modular applications and systems where every module may have entities, services, database integration, APIs, UI components and so on;
* This document introduces the basics of the module system.
* [Module development best practice guide](Best-Practices/Index.md) explains some **best practices** to develop **re-usable application modules** based on **DDD** principles and layers. A module designed based on this guide will be **database independent** and can be deployed as a **microservice** if needed.
* [Pre-built application modules](Modules/Index.md) are **ready to use** in any kind of application.
* [Module startup template](Startup-Templates/Module.md) is a jump start way to **create a new module**.
* [ABP CLI](CLI.md) has commands to support modular development.
* All other framework features are compatible to the modularity system.
## Module Class
@ -42,7 +49,7 @@ public class BlogModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
//Configure default connection string for the application
Configure<DbConnectionOptions>(options =>
Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = "......";
});

4
docs/en/Multi-Tenancy.md

@ -168,7 +168,7 @@ namespace MyCompany.MyProject
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<ConfigurationTenantStoreOptions>(options =>
Configure<AbpDefaultTenantStoreOptions>(options =>
{
options.Tenants = new[]
{
@ -214,7 +214,7 @@ namespace MyCompany.MyProject
{
var configuration = BuildConfiguration();
Configure<ConfigurationTenantStoreOptions>(configuration);
Configure<AbpDefaultTenantStoreOptions>(configuration);
}
private static IConfigurationRoot BuildConfiguration()

95
docs/en/Samples/Microservice-Demo.md

@ -24,82 +24,15 @@ This sample aims to demonstrate a simple yet complete microservice solution;
The diagram below shows the system:
![microservice-sample-diagram-2](../images/microservice-sample-diagram-2.png)
![microservice-sample-diagram-2](../images/microservice-sample-diagram-3.png)
### Source Code
You can get the source code from [the GitHub repository](https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo).
### Status
Initial version of this sample has been completed. Additional improvement are still in development.
## Running the Solution
You can either run from the **source code** or from the pre-configured **docker-compose** file.
### Using the Docker Containers
#### Pre Requirements
Running as docker containers is easier since all dependencies are pre-configured. You only need to install the [latest docker](https://docs.docker.com/compose/install/).
#### Running Containers
- Clone or download the [ABP repository](https://github.com/abpframework/abp).
- Open a command line in the `samples/MicroserviceDemo` folder of the repository.
- Pull images from Docker Hub:
```
docker-compose -f docker-compose.yml -f docker-compose.migrations.yml pull
```
- If you want to build images locally you may skip the above step and instead use build command:
```
docker-compose -f docker-compose.yml -f docker-compose.migrations.yml build
```
Building images may take a **long time** depending on your machine.
- Restore SQL Server databases:
```
docker-compose -f docker-compose.yml -f docker-compose.migrations.yml run restore-database
```
- Start the containers:
```
docker-compose up -d
```
- Add this line to the end of your `hosts` file:
```
127.0.0.1 auth-server
```
hosts file is located inside the `C:\Windows\System32\Drivers\etc\hosts` folder on Windows and `/etc/hosts` for Linux/MacOS.
#### Run the Applications
There are a few applications running in the containers you may want to explore:
* Backend Admin Application (BackendAdminApp.Host): `http://localhost:51512`
*(Used to manage users & products in the system)*
* Public Web Site (PublicWebsite.Host): `http://localhost:51513`
*(Used to list products and run/manage the blog module)*
* Authentication Server (AuthServer.Host): `http://auth-server:51511/`
*(Used as a single sign on and authentication server built with IdentityServer4)*
* Kibana UI: `http://localhost:51510`
*(Use to show/trace logs written by all services/applications/gateways)*
### Running From the Source Code
#### Pre Requirements
### Pre Requirements
To be able to run the solution from source code, following tools should be installed and running on your computer:
@ -110,19 +43,19 @@ To be able to run the solution from source code, following tools should be insta
* [ElasticSearch](https://www.elastic.co/downloads/elasticsearch) 6.6+
* [Kibana](https://www.elastic.co/downloads/kibana) 6.6+ (optional, recommended to show logs)
#### Open & Build the Visual Studio Solution
### Open & Build the Visual Studio Solution
* Open the `samples\MicroserviceDemo\MicroserviceDemo.sln` in Visual Studio 2017 (15.9.0+).
* Run `dotnet restore` from the command line inside the `samples\MicroserviceDemo` folder.
* Build the solution in Visual Studio.
#### Create Databases
### Create Databases
MongoDB database is created dynamically, however you need to create database schemas for SQL server databases. The solution is configured to use Entity Core Code First migrations, so you can easily create databases.
There are two SQL server databases in this solution.
##### MsDemo_Identity Database
#### MsDemo_Identity Database
* Right click to the `AuthServer.Host` project and click to the `Set as startup project`.
* Open the **Package Manager Console** (Tools -> Nuget Package Manager -> Package Manager Console)
@ -131,7 +64,7 @@ There are two SQL server databases in this solution.
![microservice-sample-update-database-authserver](../images/microservice-sample-update-database-authserver.png)
##### MsDemo_ProductManagement
#### MsDemo_ProductManagement
- Right click to the `ProductService.Host` project and click to the `Set as startup project`.
- Open the **Package Manager Console** (Tools -> Nuget Package Manager -> Package Manager Console)
@ -140,12 +73,13 @@ There are two SQL server databases in this solution.
![microservice-sample-update-database-products](../images/microservice-sample-update-database-products.png)
#### Run Projects
### Run Projects
Run the projects with the following order (right click to each project, set as startup project an press Ctrl+F5 to run without debug):
* AuthServer.Host
* IdentityService.Host
* TenantManagementService.Host
* BloggingService.Host
* ProductService.Host
* InternalGateway.Host
@ -160,7 +94,7 @@ When you run projects, they will add some initial demo data to their databases.
The Visual Studio solution consists of multiple projects each have different roles in the system:
![microservice-sample-solution](../images/microservice-sample-solution.png)
![microservice-sample-solution](../images/microservice-sample-solution-2.png)
### Applications
@ -183,8 +117,9 @@ Gateways are used to provide a single entry point to the applications. It can al
Microservices have no UI, but exposes some REST APIs.
- **IdentityService.Host**: Host the ABP Identity module which is used to manage users & roles. It has no additional service, but only hosts the Identity module's API.
- **BloggingService.Host**: Host the ABP Blogging module which is used to manage blog & posts (a typical blog application). It has no additional service, but only hosts the Blogging module's API.
- **IdentityService.Host**: Hosts the ABP Identity module which is used to manage users & roles. It has no additional service, but only hosts the Identity module's API.
- **TenantManagementService.Host**: Hosts the ABP Tenant Management module which is used to manage roles. It has no additional service, but only hosts the Tenant Management module's API.
- **BloggingService.Host**: Hosts the ABP Blogging module which is used to manage blog & posts (a typical blog application). It has no additional service, but only hosts the Blogging module's API.
- **ProductService.Host**: Hosts the Product module (that is inside the solution) which is used to manage products. It also contains the EF Core migrations to create/update the Product Management database schema.
### Modules
@ -195,7 +130,7 @@ Microservices have no UI, but exposes some REST APIs.
This solution is using multiple databases:
* **MsDemo_Identity**: An SQL database. Used **SQL Server** by default, but can be any DBMS supported by the EF Core. Shared by AuthServer and IdentityService. Also audit logs, permissions and settings are stored in this database (while they could easily have their own databases, shared the same database to keep it simple).
* **MsDemo_Identity**: An SQL database. Used **SQL Server** by default, but can be any DBMS supported by the EF Core. Shared by AuthServer, IdentityService and the TenantManagementService. Also audit logs, permissions and settings are stored in this database (while they could have their own databases, shared the same database to keep it simple).
* **MsDemo_ProductManagement**: An SQL database. Again, used **SQL Server** by default, but can be any DBMS supported by the EF Core. Used by the ProductService as a dedicated database.
* **MsDemo_Blogging**: A **MongoDB** database. Used by the BloggingService.
* **Elasticsearch**: Used to write logs over Serilog.
@ -1472,3 +1407,7 @@ ABP provides automatic audit logging which saves every request in detail (who is
All of the services and applications are configured to write audit logs. Audit logs are saved to the MsDemo_Identity SQL database. So, you can query all audit logs of all applications from a single point.
An Audit Log record has a `CorrelationId` property that can be used to track a request. When a service calls another service in a single web request, they both save audit logs with the same `CorrelationId`. See the `AbpAuditLogs` table in the database.
### Multi-Tenancy
The solution has been configured to provide a [multi-tenant](../Multi-Tenancy.md) system, where each tenant can have their isolated users, roles, permissions and other data.

27
docs/en/docs-nav.json

@ -54,8 +54,17 @@
]
},
{
"text": "ASP.NET Boilerplate Migration Guide",
"path": "AspNet-Boilerplate-Migration-Guide.md"
"text": "Guides",
"items": [
{
"text": "Customizing the Application Modules",
"path": "Customizing-Application-Modules-Guide.md"
},
{
"text": "Migrating from the ASP.NET Boilerplate",
"path": "AspNet-Boilerplate-Migration-Guide.md"
}
]
},
{
"text": "CLI",
@ -219,7 +228,8 @@
"text": "Domain Services"
},
{
"text": "Specifications"
"text": "Specifications",
"path": "Specifications.md"
}
]
},
@ -269,7 +279,16 @@
},
{
"text": "Tag Helpers",
"path": "AspNetCore/Tag-Helpers/Index.md"
"items":[
{
"text": "Live Demo",
"path": "AspNetCore/Tag-Helpers/Index.md"
},
{
"text": "Buttons",
"path": "AspNetCore/Tag-Helpers/Buttons.md"
}
]
},
{
"text": "Widgets",

BIN
docs/en/images/microservice-sample-diagram-3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

BIN
docs/en/images/microservice-sample-solution-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

4
docs/pt-BR/Getting-Started-Angular-Template.md

@ -29,7 +29,7 @@ A solução criada requer;
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://yarnpkg.com/)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### A Estrutura da Solução
@ -103,7 +103,7 @@ A maioria das APIs de aplicativos requer autenticação e autorização. Se voc
#### Execute o aplicativo angular (lado do cliente)
Vá para a `angular`pasta, abra um terminal de linha de comando, digite o `yarn`comando (sugerimos ao gerenciador de pacotes do [yarn](https://yarnpkg.com/) enquanto o npm install também funcionará na maioria dos casos):
Vá para a `angular`pasta, abra um terminal de linha de comando, digite o `yarn`comando (sugerimos ao gerenciador de pacotes do [yarn](https://classic.yarnpkg.com/) enquanto o npm install também funcionará na maioria dos casos):
```bash
yarn

2
docs/pt-BR/Getting-Started-AspNetCore-MVC-Template.md

@ -29,7 +29,7 @@ A solução criada requer;
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://yarnpkg.com/)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### A Estrutura da Solução

4
docs/pt-BR/Localization.md

@ -46,7 +46,7 @@ public class MyModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyModule>();
});
@ -66,7 +66,7 @@ Neste exemplo;
- Adicionado um novo recurso de localização com "en" (inglês) como a cultura padrão.
- Arquivos JSON usados para armazenar as sequências de localização.
- Os arquivos JSON são incorporados ao assembly usando `VirtualFileSystemOptions`(consulte [sistema de arquivos virtual](Virtual-File-System.md) ).
- Os arquivos JSON são incorporados ao assembly usando `AbpVirtualFileSystemOptions`(consulte [sistema de arquivos virtual](Virtual-File-System.md) ).
Os arquivos JSON estão localizados na pasta do projeto "/ Localização / Recursos / Teste", como mostrado abaixo:

4
docs/zh-Hans/AspNetCore/Client-Side-Package-Management.md

@ -5,7 +5,7 @@ ABP框架可以与任何类型的客户端包管理系统一起使用. 甚至你
但是, ABP框架最适用于**NPM/Yarn**. 默认情况下,内置模块配置为与NPM/Yarn一起使用.
最后, 我们建议[**Yarn**](https://yarnpkg.com/)而不是NPM,因为它更快,更稳定并且与NPM兼容.
最后, 我们建议[**Yarn**](https://classic.yarnpkg.com/)而不是NPM,因为它更快,更稳定并且与NPM兼容.
### @ABP NPM Packages
@ -41,7 +41,7 @@ ABP是一个模块化平台. 每个开发人员都可以创建模块, 模块应
yarn
````
虽然你可以使用`npm install`,但如前所述,建议使用[Yarn](https://yarnpkg.com/).
虽然你可以使用`npm install`,但如前所述,建议使用[Yarn](https://classic.yarnpkg.com/).
#### 贡献包

6
docs/zh-Hans/AspNetCore/Dynamic-CSharp-API-Clients.md

@ -106,12 +106,12 @@ public class MyService : ITransientDependency
### RemoteServiceOptions
默认情况下`RemoteServiceOptions`从`appsettings.json`获取.或者,你可以使用`Configure`方法来设置或重写它.如:
默认情况下`AbpRemoteServiceOptions`从`appsettings.json`获取.或者,你可以使用`Configure`方法来设置或重写它.如:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<RemoteServiceOptions>(options =>
context.Services.Configure<AbpRemoteServiceOptions>(options =>
{
options.RemoteServices.Default =
new RemoteServiceConfiguration("http://localhost:53929/");
@ -147,7 +147,7 @@ context.Services.AddHttpClientProxies(
);
````
`remoteServiceName`参数会匹配通过`RemoteServiceOptions`配置的服务端点.如果`BookStore`端点没有定义就会使用默认的`Default`端点.
`remoteServiceName`参数会匹配通过`AbpRemoteServiceOptions`配置的服务端点.如果`BookStore`端点没有定义就会使用默认的`Default`端点.
### 作为默认服务

10
docs/zh-Hans/AspNetCore/Widgets.md

@ -251,19 +251,19 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
## 部件选项
`WidgetOptions` 是 `Widget` attribute 替代, 你可以使用它去配置部件:
`AbpWidgetOptions` 是 `Widget` attribute 替代, 你可以使用它去配置部件:
```csharp
Configure<WidgetOptions>(options =>
Configure<AbpWidgetOptions>(options =>
{
options.Widgets.Add<MySimpleWidgetViewComponent>();
});
```
将上面的代码写到[模块](../Module-Development-Basics.md)的 `ConfigureServices` 方法中. `Widgetoptions` 可以完成 `Widget` attribute 的所有功能. 比如为组件添加样式:
将上面的代码写到[模块](../Module-Development-Basics.md)的 `ConfigureServices` 方法中. `AbpWidgetOptions` 可以完成 `Widget` attribute 的所有功能. 比如为组件添加样式:
````csharp
Configure<WidgetOptions>(options =>
Configure<AbpWidgetOptions>(options =>
{
options.Widgets
.Add<MySimpleWidgetViewComponent>()
@ -271,4 +271,4 @@ Configure<WidgetOptions>(options =>
});
````
> 提示: `WidgetOptions` 还可以更改现有的部件配置. 如果要修改应用程序使用的模块内的组件配置,这会很有用. 使用 `options.Widgets.Find` 获取现有的 `WidgetDefinition`.
> 提示: `AbpWidgetOptions` 还可以更改现有的部件配置. 如果要修改应用程序使用的模块内的组件配置,这会很有用. 使用 `options.Widgets.Find` 获取现有的 `WidgetDefinition`.

3
docs/zh-Hans/CLI.md

@ -1,4 +1,4 @@
# ABP CLI
# ABP CLI
ABP CLI (命令行接口) 是一个命令行工具,用来执行基于ABP解决方案的一些常见操作.
@ -109,6 +109,7 @@ abp add-module Volo.Blogging
* `--solution` 或 `-s`: 指定解决方案 (.sln) 路径. 如果未指定,CLI会尝试在当前目录中寻找.sln文件.
* `--skip-db-migrations`: 对于EF Core 数据库提供程序,它会自动添加新代码的第一次迁移 (`Add-Migration`) 并且在需要时更新数据库 (`Update-Database`). 指定此选项可跳过此操作.
* `-sp` 或 `--startup-project`: 启动项目的项目文件夹的相对路径. 默认值是当前文件夹.
* `--with-source-code`: 添加模块的源代码,而不是NuGet/NPM软件包.
### update

4
docs/zh-Hans/Exception-Handling.md

@ -204,7 +204,7 @@ throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage"
首先,在模块配置代码中将 **code-namespace** 映射至 **本地化资源**:
````C#
services.Configure<ExceptionLocalizationOptions>(options =>
services.Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Volo.Qa", typeof(QaResource));
});
@ -285,7 +285,7 @@ ABP尝试按照以下规则,自动映射常见的异常类型的HTTP状态代码
可以重写HTTP状态代码的自动映射,示例如下:
````C#
services.Configure<ExceptionHttpStatusCodeOptions>(options =>
services.Configure<AbpExceptionHttpStatusCodeOptions>(options =>
{
options.Map("Volo.Qa:010002", HttpStatusCode.Conflict);
});

2
docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md

@ -27,7 +27,7 @@ abp new Acme.BookStore
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://yarnpkg.com/)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### 解决方案结构

2
docs/zh-Hans/Localization.md

@ -46,7 +46,7 @@ public class MyModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>
Configure<AbpVirtualFileSystemOptions>(options =>
{
// "YourRootNameSpace" 是项目的根命名空间名字. 如果你的项目的根命名空间名字为空,则无需传递此参数.
options.FileSets.AddEmbedded<MyModule>("YourRootNameSpace");

2
docs/zh-Hans/Module-Development-Basics.md

@ -42,7 +42,7 @@ public class BlogModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
//为应用程序配置默认的连接字符串
Configure<DbConnectionOptions>(options =>
Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = "......";
});

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

@ -168,7 +168,7 @@ namespace MyCompany.MyProject
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<ConfigurationTenantStoreOptions>(options =>
Configure<AbpDefaultTenantStoreOptions>(options =>
{
options.Tenants = new[]
{
@ -214,7 +214,7 @@ namespace MyCompany.MyProject
{
var configuration = BuildConfiguration();
Configure<ConfigurationTenantStoreOptions>(configuration);
Configure<AbpDefaultTenantStoreOptions>(configuration);
}
private static IConfigurationRoot BuildConfiguration()

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSize.cs

@ -4,6 +4,7 @@
{
Default,
Small,
Large
Large,
ExtraLarge
}
}

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

@ -10,6 +10,8 @@
return "modal-sm";
case AbpModalSize.Large:
return "modal-lg";
case AbpModalSize.ExtraLarge:
return "modal-xl";
case AbpModalSize.Default:
return "";
default:

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NugetPackageToLocalReferenceConverter.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -52,6 +52,7 @@ namespace Volo.Abp.Cli.ProjectModification
foreach (XmlNode oldNode in nodes)
{
var tempSourceFile = sourceFile;
var oldNodeIncludeValue = oldNode?.Attributes?["Include"]?.Value;
var moduleName = nugetPackageList.FirstOrDefault(n => n.NugetPackage.Name == oldNodeIncludeValue)?.ModuleName;
@ -67,11 +68,11 @@ namespace Volo.Abp.Cli.ProjectModification
if (oldNodeIncludeValue.EndsWith(".test", StringComparison.InvariantCultureIgnoreCase) ||
oldNodeIncludeValue.EndsWith(".tests", StringComparison.InvariantCultureIgnoreCase))
{
sourceFile = "test";
tempSourceFile = "test";
}
else
{
sourceFile = "src";
tempSourceFile = "src";
}
}
else
@ -81,7 +82,7 @@ namespace Volo.Abp.Cli.ProjectModification
}
var referenceProjectPath =
$"{localPathPrefix}{moduleName}\\{sourceFile}\\{oldNodeIncludeValue}\\{oldNodeIncludeValue}.csproj";
$"{localPathPrefix}{moduleName}\\{tempSourceFile}\\{oldNodeIncludeValue}\\{oldNodeIncludeValue}.csproj";
XmlNode newNode = GetNewReferenceNode(doc, referenceProjectPath);

5
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAuthorizationHandler.cs

@ -35,6 +35,11 @@ namespace Volo.Blogging.Comments
private async Task<bool> HasDeletePermission(AuthorizationHandlerContext context, Comment resource)
{
if (resource.CreatorId != null && resource.CreatorId == context.User.FindUserId())
{
return true;
}
if (await _permissionChecker.IsGrantedAsync(context.User, BloggingPermissions.Comments.Delete))
{
return true;

5
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAuthorizationHandler.cs

@ -35,6 +35,11 @@ namespace Volo.Blogging.Posts
private async Task<bool> HasDeletePermission(AuthorizationHandlerContext context, Post resource)
{
if (resource.CreatorId != null && resource.CreatorId == context.User.FindUserId())
{
return true;
}
if (await _permissionChecker.IsGrantedAsync(context.User, BloggingPermissions.Posts.Delete))
{
return true;

3
modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json

@ -42,6 +42,7 @@
"CreationTime": "Creation Time",
"Description": "Description",
"Blogs": "Blogs",
"Tags": "Tags"
"Tags": "Tags",
"ShareOn": "Share on"
}
}

3
modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/tr.json

@ -42,6 +42,7 @@
"CreationTime": "Oluşturulma zamanı",
"Description": "Açıklama",
"Blogs": "Bloglar",
"Tags": "Etiketler"
"Tags": "Etiketler",
"ShareOn": "Paylaş"
}
}

2
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/CommentsController.cs

@ -45,7 +45,7 @@ namespace Volo.Blogging
[Route("{id}")]
public Task DeleteAsync(Guid id)
{
throw new NotImplementedException();
return _commentAppService.DeleteAsync(id);
}
}
}

22
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml

@ -32,7 +32,7 @@
<div class="hero-articles">
<div class="hero-content">
<h1 class="mb-3">
<a asp-page="./Detail" asp-route-postUrl="@Model.Post.Url" asp-route-blogShortName="@Model.BlogShortName">@Model.Post.Title</a>
<a asp-page="./Detail" asp-route-postUrl="@Model.Post.Url" asp-route-blogShortName="@Model.BlogShortName" id="PostTitle">@Model.Post.Title</a>
</h1>
<div class="article-owner">
@ -84,7 +84,7 @@
</section>
</div>
</div>
<div class="row">
<div class="col-md-8 col-lg-8 mx-auto">
<section class="post-content">
@ -94,9 +94,22 @@
</section>
</div>
</div>
<div class="row">
<div class="col-md-8 col-lg-8 mx-auto">
<hr />
<div class="mb-2 mt-1">
@(L["ShareOn"].Value + " :")
<a href="#" target="_blank" class="mr-2" id="TwitterShareLink" title="Twitter">
Twitter <i class="fa fa-twitter"></i>
</a>
<a href="#" target="_blank" class="mr-2" id="LinkedinShareLink" title="LinkedIn">
Linkedin <i class="fa fa-linkedin"></i>
</a>
<a href="#" target="_blank" class="mr-2" id="EmailShareLink" title="E-mail">
E-mail <i class="fa fa-envelope-square"></i>
</a>
</div>
@if (Model.Post.Tags.Count > 0)
{
<div class="tags">
@ -147,7 +160,7 @@
</a>
}
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Delete))
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Delete) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{
<span class="seperator">|</span>
<a href="#" class="tag deleteLink" data-deleteid="@commentWithRepliesDto.Comment.Id">
@ -317,3 +330,4 @@
</div>
</div>
</div>
<p id="BlogFullName" name="@Model.Blog.Name" hidden></p>

3
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml.cs

@ -62,8 +62,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
var editedPost = await _postAppService.UpdateAsync(Post.Id, post);
var blog = await _blogAppService.GetAsync(editedPost.BlogId);
// return Redirect(Url.Content($"~/blog/{WebUtility.UrlEncode(blog.ShortName)}/{WebUtility.UrlEncode(editedPost.Url)}"));
return RedirectToPage("/Blog/Posts/Detail", new { blogShortName = blog.ShortName, postUrl = editedPost.Url });
return RedirectToPage("/Blogs/Posts/Detail", new { blogShortName = blog.ShortName, postUrl = editedPost.Url });
}
}

32
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/detail.js

@ -2,6 +2,34 @@
var l = abp.localization.getResource('Blogging');
var initSocialShareLinks = function () {
var re = new RegExp(/^.*\//);
var rootUrl = re.exec(window.location.href);
var pageHeader = $("#PostTitle").text();
var blogName = $('#BlogFullName').attr('name');
$('#TwitterShareLink').attr('href',
'https://twitter.com/intent/tweet?text=' + encodeURI(pageHeader + " | " + blogName + " | " + window.location.href)
);
$('#LinkedinShareLink').attr('href',
'https://www.linkedin.com/shareArticle?'
+ 'url=' + encodeURI(window.location.href) + '&'
+ 'mini=true&'
+ "summary=" + encodeURI(blogName) + '&'
+ "title=" + encodeURI(pageHeader) + '&'
+ "source=" + encodeURI(rootUrl)
);
$('#EmailShareLink').attr('href',
'mailto:?'
+ 'body=' + encodeURI('I want you to look at ' + window.location.href) + '&'
+ "subject=" + encodeURI(pageHeader + ' | ' + blogName) + '&'
);
};
$('div .replyForm').hide();
$('div .editForm').hide();
@ -135,4 +163,8 @@
}, 500);
}
$(".post-content a[href^='http']").attr('target', '_blank');
initSocialShareLinks();
})(jQuery);

3
modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\..\configureawait.props" />
@ -8,6 +8,7 @@
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<UserSecretsId>5f11b41f-0025-4fe6-ab97-60ec1bd4e8c2</UserSecretsId>
</PropertyGroup>
<ItemGroup>

6
modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs

@ -33,6 +33,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Volo.Abp.Account;
using Volo.Abp.Validation.Localization;
using Volo.Docs.Documents.FullSearch.Elastic;
namespace VoloDocs.Web
{
@ -71,6 +72,11 @@ namespace VoloDocs.Web
options.RoutePrefix = null;
});
Configure<DocsElasticSearchOptions>(options =>
{
options.Enable = true;
});
Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = configuration["ConnectionString"];

5
modules/docs/app/VoloDocs.Web/appsettings.json

@ -1,4 +1,7 @@
{
"ConnectionString": "Server=localhost;Database=VoloDocs;Trusted_Connection=True;MultipleActiveResultSets=true",
"LogoUrl": "/assets/images/Logo.png"
"LogoUrl": "/assets/images/Logo.png",
"ElasticSearch": {
"Url": "http://localhost:9200"
}
}

2
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/IDocumentAdminAppService.cs

@ -8,5 +8,7 @@ namespace Volo.Docs.Admin.Documents
Task PullAllAsync(PullAllDocumentInput input);
Task PullAsync(PullDocumentInput input);
Task ReindexAsync();
}
}

22
modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs

@ -7,6 +7,7 @@ using Newtonsoft.Json;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Docs.Documents;
using Volo.Docs.Documents.FullSearch.Elastic;
using Volo.Docs.Projects;
namespace Volo.Docs.Admin.Documents
@ -18,16 +19,19 @@ namespace Volo.Docs.Admin.Documents
private readonly IDocumentRepository _documentRepository;
private readonly IDocumentSourceFactory _documentStoreFactory;
private readonly IDistributedCache<DocumentUpdateInfo> _documentUpdateCache;
private readonly IDocumentFullSearch _documentFullSearch;
public DocumentAdminAppService(IProjectRepository projectRepository,
IDocumentRepository documentRepository,
IDocumentSourceFactory documentStoreFactory,
IDistributedCache<DocumentUpdateInfo> documentUpdateCache)
IDistributedCache<DocumentUpdateInfo> documentUpdateCache,
IDocumentFullSearch documentFullSearch)
{
_projectRepository = projectRepository;
_documentRepository = documentRepository;
_documentStoreFactory = documentStoreFactory;
_documentUpdateCache = documentUpdateCache;
_documentFullSearch = documentFullSearch;
}
public async Task PullAllAsync(PullAllDocumentInput input)
@ -80,6 +84,22 @@ namespace Volo.Docs.Admin.Documents
await UpdateDocumentUpdateInfoCache(sourceDocument);
}
public async Task ReindexAsync()
{
var docs = await _documentRepository.GetListAsync();
var projects = await _projectRepository.GetListAsync();
foreach (var doc in docs)
{
var project = projects.FirstOrDefault(x => x.Id == doc.ProjectId);
if (project != null && (doc.FileName == project.NavigationDocumentName || doc.FileName == project.ParametersDocumentName))
{
continue;
}
await _documentFullSearch.AddOrUpdateAsync(doc);
}
}
private async Task UpdateDocumentUpdateInfoCache(Document document)
{
var cacheKey = $"DocumentUpdateInfo{document.ProjectId}#{document.Name}#{document.LanguageCode}#{document.Version}";

7
modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs

@ -32,5 +32,12 @@ namespace Volo.Docs.Admin
{
return _documentAdminAppService.PullAsync(input);
}
[HttpPost]
[Route("Reindex")]
public Task ReindexAsync()
{
return _documentAdminAppService.ReindexAsync();
}
}
}

19
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentSearchInput.cs

@ -0,0 +1,19 @@
using System;
namespace Volo.Docs.Documents
{
public class DocumentSearchInput
{
public string Context { get; set; }
public Guid ProjectId { get; set; }
public string LanguageCode { get; set; }
public string Version { get; set; }
public int? SkipCount { get; set; }
public int? MaxResultCount { get; set; }
}
}

24
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentSearchOutput.cs

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Volo.Docs.Documents
{
public class DocumentSearchOutput
{
public DocumentSearchOutput()
{
Highlight = new List<string>();
}
public string Name { get; set; }
public string FileName { get; set; }
public string Version { get; set; }
public string LanguageCode { get; set; }
public List<string> Highlight { get; set; }
}
}

5
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/IDocumentAppService.cs

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
@ -14,5 +15,9 @@ namespace Volo.Docs.Documents
Task<DocumentParametersDto> GetParametersAsync(GetParametersDocumentInput input);
Task<DocumentResourceDto> GetResourceAsync(GetDocumentResourceInput input);
Task<List<DocumentSearchOutput>> SearchAsync(DocumentSearchInput input);
Task<bool> FullSearchEnabledAsync();
}
}

32
modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs

@ -5,8 +5,10 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Volo.Abp.Caching;
using Volo.Docs.Documents.FullSearch.Elastic;
using Volo.Docs.Projects;
namespace Volo.Docs.Documents
@ -20,6 +22,9 @@ namespace Volo.Docs.Documents
protected IDistributedCache<DocumentResourceDto> ResourceCache { get; }
protected IDistributedCache<DocumentUpdateInfo> DocumentUpdateCache { get; }
protected IHostEnvironment HostEnvironment { get; }
private readonly IDocumentFullSearch _documentFullSearch;
private readonly DocsElasticSearchOptions _docsElasticSearchOptions;
public DocumentAppService(
IProjectRepository projectRepository,
IDocumentRepository documentRepository,
@ -27,7 +32,9 @@ namespace Volo.Docs.Documents
IDistributedCache<LanguageConfig> languageCache,
IDistributedCache<DocumentResourceDto> resourceCache,
IDistributedCache<DocumentUpdateInfo> documentUpdateCache,
IHostEnvironment hostEnvironment)
IHostEnvironment hostEnvironment,
IDocumentFullSearch documentFullSearch,
IOptions<DocsElasticSearchOptions> docsElasticSearchOptions)
{
_projectRepository = projectRepository;
_documentRepository = documentRepository;
@ -36,6 +43,8 @@ namespace Volo.Docs.Documents
ResourceCache = resourceCache;
DocumentUpdateCache = documentUpdateCache;
HostEnvironment = hostEnvironment;
_documentFullSearch = documentFullSearch;
_docsElasticSearchOptions = docsElasticSearchOptions.Value;
}
public virtual async Task<DocumentWithDetailsDto> GetAsync(GetDocumentInput input)
@ -124,6 +133,27 @@ namespace Volo.Docs.Documents
);
}
public async Task<List<DocumentSearchOutput>> SearchAsync(DocumentSearchInput input)
{
var project = await _projectRepository.GetAsync(input.ProjectId);
var esDocs = await _documentFullSearch.SearchAsync(input.Context, project.Id, input.LanguageCode, input.Version);
return esDocs.Select(esDoc => new DocumentSearchOutput//TODO: auto map
{
Name = esDoc.Name,
FileName = esDoc.FileName,
Version = esDoc.Version,
LanguageCode = esDoc.LanguageCode,
Highlight = esDoc.Highlight
}).ToList();
}
public async Task<bool> FullSearchEnabledAsync()
{
return await Task.FromResult(_docsElasticSearchOptions.Enable);
}
public async Task<DocumentParametersDto> GetParametersAsync(GetParametersDocumentInput input)
{
var project = await _projectRepository.GetAsync(input.ProjectId);

7
modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/DocsDomainErrorCodes.cs

@ -0,0 +1,7 @@
namespace Volo.Docs
{
public static class DocsDomainErrorCodes
{
public const string ElasticSearchNotEnabled = "Volo.Docs.Domain:010001";
}
}

1
modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj

@ -18,6 +18,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.0" />
<PackageReference Include="Octokit" Version="0.36.0" />
<PackageReference Include="NEST" Version="7.5.1" />
</ItemGroup>
<ItemGroup>

16
modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs

@ -1,10 +1,14 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Domain;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
using Volo.Abp.VirtualFileSystem;
using Volo.Docs.Documents;
using Volo.Docs.Documents.FullSearch.Elastic;
using Volo.Docs.FileSystem.Documents;
using Volo.Docs.GitHub.Documents;
using Volo.Docs.Localization;
@ -43,5 +47,17 @@ namespace Volo.Docs
client.Timeout = TimeSpan.FromMilliseconds(15000);
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
using (var scope = context.ServiceProvider.CreateScope())
{
if (scope.ServiceProvider.GetRequiredService<IOptions<DocsElasticSearchOptions>>().Value.Enable)
{
var documentFullSearch = scope.ServiceProvider.GetRequiredService<IDocumentFullSearch>();
AsyncHelper.RunSync(() => documentFullSearch.CreateIndexIfNeededAsync());
}
}
}
}
}

27
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/DefaultElasticClientProvider.cs

@ -0,0 +1,27 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Nest;
using Volo.Abp.DependencyInjection;
namespace Volo.Docs.Documents.FullSearch.Elastic
{
public class DefaultElasticClientProvider : IElasticClientProvider, ISingletonDependency
{
protected readonly DocsElasticSearchOptions Options;
protected readonly IConfiguration Configuration;
public DefaultElasticClientProvider(IOptions<DocsElasticSearchOptions> options, IConfiguration configuration)
{
Configuration = configuration;
Options = options.Value;
}
public virtual IElasticClient GetClient()
{
var node = new Uri(Configuration["ElasticSearch:Url"]);
var settings = new ConnectionSettings(node).DefaultIndex(Options.IndexName);
return new ElasticClient(settings);
}
}
}

15
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/DocsElasticSearchOptions.cs

@ -0,0 +1,15 @@
namespace Volo.Docs.Documents.FullSearch.Elastic
{
public class DocsElasticSearchOptions
{
public DocsElasticSearchOptions()
{
Enable = false;
IndexName = "abp_documents";
}
public bool Enable { get; set; }
public string IndexName { get; set; }
}
}

49
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/DocumentChangedEventHandler.cs

@ -0,0 +1,49 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
namespace Volo.Docs.Documents.FullSearch.Elastic
{
public class DocumentChangedEventHandler : ILocalEventHandler<EntityCreatedEventData<Document>>,
ILocalEventHandler<EntityUpdatedEventData<Document>>,
ILocalEventHandler<EntityDeletedEventData<Document>>,
ITransientDependency
{
private readonly DocsElasticSearchOptions _options;
private readonly IDocumentFullSearch _documentFullSearch;
public DocumentChangedEventHandler(IDocumentFullSearch documentFullSearch, IOptions<DocsElasticSearchOptions> options)
{
_documentFullSearch = documentFullSearch;
_options = options.Value;
}
public async Task HandleEventAsync(EntityCreatedEventData<Document> eventData)
{
await AddOrUpdate(eventData.Entity);
}
public async Task HandleEventAsync(EntityUpdatedEventData<Document> eventData)
{
await AddOrUpdate(eventData.Entity);
}
private async Task AddOrUpdate(Document document)
{
if (_options.Enable)
{
await _documentFullSearch.AddOrUpdateAsync(document);
}
}
public async Task HandleEventAsync(EntityDeletedEventData<Document> eventData)
{
if (_options.Enable)
{
await _documentFullSearch.DeleteAsync(eventData.Entity.Id);
}
}
}
}

189
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs

@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elasticsearch.Net;
using Microsoft.Extensions.Options;
using Nest;
using Volo.Abp;
using Volo.Abp.Domain.Services;
namespace Volo.Docs.Documents.FullSearch.Elastic
{
public class ElasticDocumentFullSearch : DomainService, IDocumentFullSearch
{
private readonly IElasticClientProvider _clientProvider;
private readonly DocsElasticSearchOptions _options;
public ElasticDocumentFullSearch(IElasticClientProvider clientProvider, IOptions<DocsElasticSearchOptions> options)
{
_clientProvider = clientProvider;
_options = options.Value;
}
public async Task CreateIndexIfNeededAsync(CancellationToken cancellationToken = default)
{
CheckEsEnabled();
var client = _clientProvider.GetClient();
var existsResponse = await client.Indices.ExistsAsync(_options.IndexName, ct: cancellationToken);
HandleError(existsResponse);
if (!existsResponse.Exists)
{
HandleError(await client.Indices.CreateAsync(_options.IndexName, c => c
.Map<EsDocument>(m =>
{
return m
.Properties(p => p
.Keyword(x => x.Name(d => d.Id))
.Keyword(x => x.Name(d => d.ProjectId))
.Keyword(x => x.Name(d => d.Name))
.Keyword(x => x.Name(d => d.FileName))
.Keyword(x => x.Name(d => d.Version))
.Keyword(x => x.Name(d => d.LanguageCode))
.Text(x => x.Name(d => d.Content)));
}), cancellationToken));
}
}
public async Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default)
{
CheckEsEnabled();
var client = _clientProvider.GetClient();
var existsResponse = await client.DocumentExistsAsync<EsDocument>(DocumentPath<EsDocument>.Id(document.Id),
x => x.Index(_options.IndexName), cancellationToken);
HandleError(existsResponse);
var esDocument = new EsDocument
{
Id = document.Id,
ProjectId = document.ProjectId,
Name = document.Name,
FileName = document.FileName,
Content = document.Content,
LanguageCode = document.LanguageCode,
Version = document.Version
};
if (!existsResponse.Exists)
{
HandleError(await client.IndexAsync<EsDocument>(esDocument,
x => x.Id(document.Id).Index(_options.IndexName), cancellationToken));
}
else
{
HandleError(await client.UpdateAsync<EsDocument>(DocumentPath<EsDocument>.Id(document.Id),
x => x.Doc(esDocument).Index(_options.IndexName), cancellationToken));
}
}
public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default)
{
CheckEsEnabled();
HandleError(await _clientProvider.GetClient()
.DeleteAsync(DocumentPath<Document>.Id(id), x => x.Index(_options.IndexName), cancellationToken));
}
public async Task<List<EsDocument>> SearchAsync(string context, Guid projectId, string languageCode,
string version, int? skipCount = null, int? maxResultCount = null,
CancellationToken cancellationToken = default)
{
CheckEsEnabled();
var request = new SearchRequest
{
Size = maxResultCount ?? 10,
From = skipCount ?? 0,
Query = new BoolQuery
{
Must = new QueryContainer[]
{
new MatchQuery
{
Field = "content",
Query = context
}
},
Filter = new QueryContainer[]
{
new BoolQuery
{
Must = new QueryContainer[]
{
new TermQuery
{
Field = "projectId",
Value = projectId
},
new TermQuery
{
Field = "version",
Value = version
},
new TermQuery
{
Field = "languageCode",
Value = languageCode
}
}
}
}
},
Highlight = new Highlight
{
PreTags = new[] { "<highlight>" },
PostTags = new[] { "</highlight>" },
Fields = new Dictionary<Field, IHighlightField>
{
{
"content", new HighlightField()
}
}
}
};
//var json = _clientProvider.GetClient().RequestResponseSerializer.SerializeToString(request);
var response = await _clientProvider.GetClient().SearchAsync<EsDocument>(request, cancellationToken);
HandleError(response);
var docs = new List<EsDocument>();
foreach (var hit in response.Hits)
{
var doc = hit.Source;
if (hit.Highlight.ContainsKey("content"))
{
doc.Highlight = new List<string>();
doc.Highlight.AddRange(hit.Highlight["content"]);
}
docs.Add(doc);
}
return docs;
}
protected void HandleError(IElasticsearchResponse response)
{
if (!response.ApiCall.Success)
{
throw response.ApiCall.OriginalException;
}
}
protected void CheckEsEnabled()
{
if (!_options.Enable)
{
throw new BusinessException(DocsDomainErrorCodes.ElasticSearchNotEnabled);
}
}
}
}

29
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/EsDocument.cs

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
namespace Volo.Docs.Documents.FullSearch.Elastic
{
public class EsDocument
{
public EsDocument()
{
Highlight = new List<string>();
}
public Guid Id { get; set; }
public Guid ProjectId { get; set; }
public string Name { get; set; }
public string FileName { get; set; }
public string Version { get; set; }
public string LanguageCode { get; set; }
public string Content { get; set; }
public List<string> Highlight { get; set; }
}
}

20
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IDocumentFullSearch.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Docs.Documents.FullSearch.Elastic
{
public interface IDocumentFullSearch
{
Task CreateIndexIfNeededAsync(CancellationToken cancellationToken = default);
Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default);
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default);
Task<List<EsDocument>> SearchAsync(string context, Guid projectId, string languageCode,
string version, int? skipCount = null, int? maxResultCount = null,
CancellationToken cancellationToken = default);
}
}

9
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IElasticClientProvider.cs

@ -0,0 +1,9 @@
using Nest;
namespace Volo.Docs.Documents.FullSearch.Elastic
{
public interface IElasticClientProvider
{
IElasticClient GetClient();
}
}

2
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json

@ -17,6 +17,8 @@
"NavigationDocumentNotFound": "This version does not have a navigation document!",
"DocumentNotFoundInSelectedLanguage": "Document in the language you wanted is not found. Document in the default language is shown.",
"FilterTopics": "Filter topics",
"FullSearch": "Search in documents",
"Volo.Docs.Domain:010001": "Elastic search is not enabled.",
"MultipleVersionDocumentInfo": "This document has multiple versions. Select the options best fit for you.",
"New": "New",
"Upd": "Upd",

2
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json

@ -17,6 +17,8 @@
"NavigationDocumentNotFound": "这个版本没有导航文件!",
"DocumentNotFoundInSelectedLanguage": "本文档不适用于所选语言, 将以默认语言显示文档.",
"FilterTopics": "过滤主题",
"FullSearch": "搜索文档",
"Volo.Docs.Domain:010001": "Elastic search未启用.",
"New": "新文档",
"Upd": "更新",
"NewExplanation": "在最近两周内创建.",

1
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json

@ -17,6 +17,7 @@
"NavigationDocumentNotFound": "這個版本沒有導覽文件!",
"DocumentNotFoundInSelectedLanguage": "本文件不適用於所選語系,將以預設語系顯示.",
"FilterTopics": "過濾主題",
"FullSearch": "搜索文件",
"New": "新文檔",
"Upd": "更新",
"NewExplanation": "在最近兩周內創建.",

17
modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Documents/DocsDocumentController.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
@ -46,6 +47,20 @@ namespace Volo.Docs.Documents
return DocumentAppService.GetResourceAsync(input);
}
[HttpPost]
[Route("search")]
public Task<List<DocumentSearchOutput>> SearchAsync(DocumentSearchInput input)
{
return DocumentAppService.SearchAsync(input);
}
[HttpGet]
[Route("full-search-enabled")]
public Task<bool> FullSearchEnabledAsync()
{
return DocumentAppService.FullSearchEnabledAsync();
}
[HttpGet]
[Route("parameters")]
public Task<DocumentParametersDto> GetParametersAsync(GetParametersDocumentInput input)

3
modules/docs/src/Volo.Docs.Web/DocsWebModule.cs

@ -1,4 +1,4 @@
using System;
using System;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@ -61,6 +61,7 @@ namespace Volo.Docs
options.Conventions.AddPageRoute("/Documents/Project/Index", routePrefix + "{projectName}");
options.Conventions.AddPageRoute("/Documents/Project/Index", routePrefix + "{languageCode}/{projectName}");
options.Conventions.AddPageRoute("/Documents/Project/Index", routePrefix + "{languageCode}/{projectName}/{version}/{*documentName}");
options.Conventions.AddPageRoute("/Documents/Search", routePrefix + "search/{languageCode}/{projectName}/{version}/{*keyword}");
});
context.Services.AddAutoMapperObjectMapper<DocsWebModule>();

18
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml

@ -135,18 +135,32 @@
}
</div>
<div class="docs-version mb-4">
<div class="docs-version">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text"><i class="fa fa-filter"></i></label>
</div>
<input class="form-control" type="search" placeholder="@L["FilterTopics"].Value" aria-label="Filter">
<input class="form-control" id="filter" type="search" data-search-url="@Model." placeholder="@L["FilterTopics"].Value" aria-label="Filter">
</div>
</div>
</div>
@if (Model.FullSearchEnabled)
{
<div class="docs-version mb-4">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text"><i class="fa fa-filter"></i></label>
</div>
<input class="form-control" id="fullsearch" type="search" data-fullsearch-url="/search/@Model.LanguageCode/@Model.ProjectName/@Model.Version/" placeholder="@L["FullSearch"].Value" aria-label="Filter">
</div>
</div>
</div>
}
@if (Model.Navigation == null || !Model.Navigation.HasChildItems)
{

3
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs

@ -64,6 +64,8 @@ namespace Volo.Docs.Pages.Documents.Project
public DocumentParametersDto DocumentPreferences { get; set; }
public DocumentRenderParameters UserPreferences { get; set; } = new DocumentRenderParameters();
public bool FullSearchEnabled { get; set; }
private readonly IDocumentAppService _documentAppService;
private readonly IDocumentToHtmlConverterFactory _documentToHtmlConverterFactory;
@ -106,6 +108,7 @@ namespace Volo.Docs.Pages.Documents.Project
{
DocumentsUrlPrefix = _uiOptions.RoutePrefix;
ShowProjectsCombobox = _uiOptions.ShowProjectsCombobox;
FullSearchEnabled = await _documentAppService.FullSearchEnabledAsync();
try
{

10
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js

@ -1,4 +1,4 @@
(function ($) {
(function ($) {
$(function () {
var initNavigationFilter = function (navigationContainerId) {
@ -56,13 +56,19 @@
});
};
$(".docs-page .docs-tree-list input[type='search']").keyup(function (e) {
$("#filter").keyup(function (e) {
filterDocumentItems(e.target.value);
if (e.key === "Enter") {
gotoFilteredDocumentIfThereIsOnlyOne();
}
});
$("#fullsearch").keyup(function (e) {
if (e.key === "Enter") {
window.open($(this).data("fullsearch-url") + this.value);
}
});
};
var initAnchorTags = function (container) {

67
modules/docs/src/Volo.Docs.Web/Pages/Documents/Search.cshtml

@ -0,0 +1,67 @@
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Volo.Docs.Localization
@using Volo.Docs.Pages.Documents
@inject IHtmlLocalizer<DocsResource> L
@inject IThemeManager ThemeManager
@inject IPageLayout PageLayout
@model SearchModel
@{
Layout = ThemeManager.CurrentTheme.GetEmptyLayout();
}
@section styles {
<style>
highlight {
font-weight: bold;
color: red;
font-style: italic;
}
</style>
}
<div class="container">
<form method="get" action="/search/@Model.LanguageCode/@Model.ProjectName/@Model.Version/" class="mt-4">
<input type="text" asp-for="@Model.KeyWord" class="form-control" />
<button type="submit" class="btn-block btn-primary btn-lg mt-3">Search</button>
</form>
<div class="my-3 p-3 bg-white rounded">
<h6 class="border-bottom pb-4 mb-0">Search results</h6>
@foreach (var docs in Model.SearchOutputs)
{
<div class="media text-muted pt-3">
<div class="media-body pb-3 mb-0 small border-bottom">
<div class="list-group">
@functions
{
string RemoveFileExtensionFromPath(string path)
{
if (path == null)
{
return null;
}
return path.EndsWith("." + @Model.Project.Format)
? path.Left(path.Length - Model.Project.Format.Length - 1)
: path;
}
}
<a href="/@Model.LanguageCode/@Model.ProjectName/@Model.Version/@RemoveFileExtensionFromPath(docs.Name)">
<h3>@RemoveFileExtensionFromPath(docs.Name)</h3></a>
@foreach (var highlight in docs.Highlight)
{
<p class="list-group-item list-group-item-action">@Html.Raw(highlight)</p>
}
</div>
</div>
</div>
}
</div>
</div>

73
modules/docs/src/Volo.Docs.Web/Pages/Documents/Search.cshtml.cs

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Docs.Documents;
using Volo.Docs.HtmlConverting;
using Volo.Docs.Models;
using Volo.Docs.Projects;
namespace Volo.Docs.Pages.Documents
{
public class SearchModel : PageModel
{
[BindProperty(SupportsGet = true)]
public string ProjectName { get; set; }
[BindProperty(SupportsGet = true)]
public string Version { get; set; }
[BindProperty(SupportsGet = true)]
public string LanguageCode { get; set; }
[BindProperty(SupportsGet = true)]
public string KeyWord { get; set; }
public ProjectDto Project { get; set; }
private readonly IProjectAppService _projectAppService;
private readonly IDocumentAppService _documentAppService;
public SearchModel(IProjectAppService projectAppService,
IDocumentAppService documentAppService)
{
_projectAppService = projectAppService;
_documentAppService = documentAppService;
}
public List<DocumentSearchOutput> SearchOutputs { get; set; } = new List<DocumentSearchOutput>();
public async Task<IActionResult> OnGetAsync(string keyword)
{
if (!await _documentAppService.FullSearchEnabledAsync())
{
return RedirectToPage("Index");
}
KeyWord = keyword;
Project = await _projectAppService.GetAsync(ProjectName);
var output = await _projectAppService.GetVersionsAsync(Project.ShortName);
var versions = output.Items.ToList();
if (versions.Any() && string.Equals(Version, DocsAppConsts.Latest, StringComparison.OrdinalIgnoreCase))
{
Version = versions.First().Name;
}
SearchOutputs = await _documentAppService.SearchAsync(new DocumentSearchInput
{
ProjectId = Project.Id,
Context = KeyWord,
LanguageCode = LanguageCode,
Version = Version
});
return Page();
}
}
}

12
modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantEto.cs

@ -0,0 +1,12 @@
using System;
namespace Volo.Abp.TenantManagement
{
[Serializable]
public class TenantEto
{
public Guid Id { get; set; }
public string Name { get; set; }
}
}

2
modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainMappingProfile.cs

@ -23,6 +23,8 @@ namespace Volo.Abp.TenantManagement
return connStrings;
});
});
CreateMap<Tenant, TenantEto>();
}
}
}

7
modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs

@ -2,6 +2,7 @@
using Volo.Abp.AutoMapper;
using Volo.Abp.Data;
using Volo.Abp.Domain;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.UI;
@ -19,10 +20,16 @@ namespace Volo.Abp.TenantManagement
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<AbpTenantManagementDomainModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<AbpTenantManagementDomainMappingProfile>(validate: true);
});
Configure<AbpDistributedEventBusOptions>(options =>
{
options.EtoMappings.Add<Tenant, TenantEto>();
});
}
}
}

2
npm/package.json

@ -1,5 +1,5 @@
{
"version": "2.2.0",
"version": "2.3.0",
"scripts": {
"lerna": "lerna",
"gulp:app": "node run-gulp-script.js ../templates/app/aspnet-core",

13
npm/preview-publish.ps1

@ -1,5 +1,9 @@
param(
[string]$Version
[string]$Version,
[string]$User,
[string]$Pass,
[string]$Email,
[string]$Registry
)
npm install
@ -11,12 +15,17 @@ if(-Not $Version) {
$Version = $NextVersion;
}
if(-Not $Registry) {
$Registry = "http://localhost:4873";
}
$commands = (
"npm-cli-login -u $User -p $Pass -e $Email -r $Registry"
"cd ng-packs\scripts",
"npm install",
"npm run publish-packages -- --nextVersion $Version --preview",
"cd ../../",
"yarn lerna publish $Version --no-push --yes --no-git-reset --no-commit-hooks --no-git-tag-version --force-publish --dist-tag preview --registry http://localhost:4873"
"yarn lerna publish $Version --no-push --yes --no-git-reset --no-commit-hooks --no-git-tag-version --force-publish --dist-tag preview --registry $Registry"
)
foreach ($command in $commands) {

20
samples/MicroserviceDemo/MicroserviceDemo.sln

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.168
# Visual Studio Version 16
VisualStudioVersion = 16.0.29609.76
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "microservices", "microservices", "{3B26D176-390B-4F51-BE52-E9B422C28A88}"
EndProject
@ -61,6 +61,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleClientDemo", "applic
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublicWebSite.Host", "applications\PublicWebSite.Host\PublicWebSite.Host.csproj", "{29A3A4EF-DEC4-4E43-B6B8-D565667F85EC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TenantManagementService.Host", "microservices\TenantManagementService.Host\TenantManagementService.Host.csproj", "{3E1DDEDA-A897-470F-81C2-5A3AFA6D0B61}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{2B3B460C-0293-44A1-BEF4-91A778CE73CE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsDemo.Shared", "shared\MsDemo.Shared\MsDemo.Shared.csproj", "{D4549E30-18CC-4157-B994-C0097216C2C8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -155,6 +161,14 @@ Global
{29A3A4EF-DEC4-4E43-B6B8-D565667F85EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29A3A4EF-DEC4-4E43-B6B8-D565667F85EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29A3A4EF-DEC4-4E43-B6B8-D565667F85EC}.Release|Any CPU.Build.0 = Release|Any CPU
{3E1DDEDA-A897-470F-81C2-5A3AFA6D0B61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E1DDEDA-A897-470F-81C2-5A3AFA6D0B61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E1DDEDA-A897-470F-81C2-5A3AFA6D0B61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E1DDEDA-A897-470F-81C2-5A3AFA6D0B61}.Release|Any CPU.Build.0 = Release|Any CPU
{D4549E30-18CC-4157-B994-C0097216C2C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4549E30-18CC-4157-B994-C0097216C2C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4549E30-18CC-4157-B994-C0097216C2C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4549E30-18CC-4157-B994-C0097216C2C8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -185,6 +199,8 @@ Global
{2199D857-C926-4948-A7F7-A11E344F90AA} = {8F6834D7-E6FA-4A04-83BB-955F68EA0A0A}
{BB8FA269-460D-42BE-90A0-E97D6A3FDEB1} = {8F6834D7-E6FA-4A04-83BB-955F68EA0A0A}
{29A3A4EF-DEC4-4E43-B6B8-D565667F85EC} = {8F6834D7-E6FA-4A04-83BB-955F68EA0A0A}
{3E1DDEDA-A897-470F-81C2-5A3AFA6D0B61} = {3B26D176-390B-4F51-BE52-E9B422C28A88}
{D4549E30-18CC-4157-B994-C0097216C2C8} = {2B3B460C-0293-44A1-BEF4-91A778CE73CE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {37474F0D-2E52-4D2F-B39B-7FE3FF31B4EC}

6
samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
@ -32,7 +32,11 @@
<ProjectReference Include="..\..\..\..\modules\account\src\Volo.Abp.Account.Application\Volo.Abp.Account.Application.csproj" />
<ProjectReference Include="..\..\..\..\modules\audit-logging\src\Volo.Abp.AuditLogging.EntityFrameworkCore\Volo.Abp.AuditLogging.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.EntityFrameworkCore\Volo.Abp.PermissionManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\modules\feature-management\src\Volo.Abp.FeatureManagement.EntityFrameworkCore\Volo.Abp.FeatureManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\modules\setting-management\src\Volo.Abp.SettingManagement.EntityFrameworkCore\Volo.Abp.SettingManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.Application.Contracts\Volo.Abp.TenantManagement.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.EntityFrameworkCore\Volo.Abp.TenantManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\shared\MsDemo.Shared\MsDemo.Shared.csproj" />
</ItemGroup>
<ItemGroup>

8
samples/MicroserviceDemo/applications/AuthServer.Host/AuthServerDataSeeder.cs

@ -10,6 +10,7 @@ using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.IdentityServer.Clients;
using Volo.Abp.IdentityServer.IdentityResources;
using Volo.Abp.PermissionManagement;
using Volo.Abp.TenantManagement;
using Volo.Abp.Uow;
namespace AuthServer.Host
@ -57,6 +58,7 @@ namespace AuthServer.Host
};
await CreateApiResourceAsync("IdentityService", commonApiUserClaims);
await CreateApiResourceAsync("TenantManagementService", commonApiUserClaims);
await CreateApiResourceAsync("BloggingService", commonApiUserClaims);
await CreateApiResourceAsync("ProductService", commonApiUserClaims);
await CreateApiResourceAsync("InternalGateway", commonApiUserClaims);
@ -106,15 +108,15 @@ namespace AuthServer.Host
await CreateClientAsync(
"console-client-demo",
new[] { "BloggingService", "IdentityService", "InternalGateway", "ProductService" },
new[] { "BloggingService", "IdentityService", "InternalGateway", "ProductService", "TenantManagementService" },
new[] { "client_credentials", "password" },
commonSecret,
permissions: new[] { IdentityPermissions.Users.Default, "ProductManagement.Product" }
permissions: new[] { IdentityPermissions.Users.Default, TenantManagementPermissions.Tenants.Default, "ProductManagement.Product" }
);
await CreateClientAsync(
"backend-admin-app-client",
commonScopes.Union(new[] { "BackendAdminAppGateway", "IdentityService", "ProductService" }),
commonScopes.Union(new[] { "BackendAdminAppGateway", "IdentityService", "ProductService", "TenantManagementService" }),
new[] { "hybrid" },
commonSecret,
permissions: new[] { IdentityPermissions.Users.Default, "ProductManagement.Product" },

21
samples/MicroserviceDemo/applications/AuthServer.Host/AuthServerHostModule.cs

@ -2,6 +2,7 @@ using AuthServer.Host.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using MsDemo.Shared;
using StackExchange.Redis;
using Volo.Abp;
using Volo.Abp.Account;
@ -9,7 +10,6 @@ using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.Auditing;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Autofac;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
@ -20,9 +20,11 @@ using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Volo.Abp.Threading;
namespace AuthServer.Host
@ -39,8 +41,10 @@ namespace AuthServer.Host
typeof(AbpIdentityServerEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(AbpAccountWebIdentityServerModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule)
)]
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
typeof(AbpTenantManagementApplicationContractsModule)
)]
public class AuthServerHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
@ -52,6 +56,11 @@ namespace AuthServer.Host
options.AddDefaultRepositories();
});
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MsDemoConsts.IsMultiTenancyEnabled;
});
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
@ -86,6 +95,10 @@ namespace AuthServer.Host
app.UseCorrelationId();
app.UseVirtualFiles();
app.UseRouting();
if (MsDemoConsts.IsMultiTenancyEnabled)
{
app.UseMultiTenancy();
}
app.UseIdentityServer();
app.UseAbpRequestLocalization();
app.UseAuditing();

4
samples/MicroserviceDemo/applications/AuthServer.Host/EntityFrameworkCore/AuthServerDbContext.cs

@ -1,10 +1,12 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace AuthServer.Host.EntityFrameworkCore
{
@ -25,6 +27,8 @@ namespace AuthServer.Host.EntityFrameworkCore
modelBuilder.ConfigureAuditLogging();
modelBuilder.ConfigurePermissionManagement();
modelBuilder.ConfigureSettingManagement();
modelBuilder.ConfigureTenantManagement();
modelBuilder.ConfigureFeatureManagement();
}
}
}

1675
samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20200304125631_Added_Tenant_Management.Designer.cs

File diff suppressed because it is too large

65
samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20200304125631_Added_Tenant_Management.cs

@ -0,0 +1,65 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace AuthServer.Host.Migrations
{
public partial class Added_Tenant_Management : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AbpTenants",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
ExtraProperties = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
CreationTime = table.Column<DateTime>(nullable: false),
CreatorId = table.Column<Guid>(nullable: true),
LastModificationTime = table.Column<DateTime>(nullable: true),
LastModifierId = table.Column<Guid>(nullable: true),
IsDeleted = table.Column<bool>(nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(nullable: true),
DeletionTime = table.Column<DateTime>(nullable: true),
Name = table.Column<string>(maxLength: 64, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpTenants", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpTenantConnectionStrings",
columns: table => new
{
TenantId = table.Column<Guid>(nullable: false),
Name = table.Column<string>(maxLength: 64, nullable: false),
Value = table.Column<string>(maxLength: 1024, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpTenantConnectionStrings", x => new { x.TenantId, x.Name });
table.ForeignKey(
name: "FK_AbpTenantConnectionStrings_AbpTenants_TenantId",
column: x => x.TenantId,
principalTable: "AbpTenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AbpTenants_Name",
table: "AbpTenants",
column: "Name");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AbpTenantConnectionStrings");
migrationBuilder.DropTable(
name: "AbpTenants");
}
}
}

1706
samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20200304155131_Added_Feature_Management.Designer.cs

File diff suppressed because it is too large

37
samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20200304155131_Added_Feature_Management.cs

@ -0,0 +1,37 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace AuthServer.Host.Migrations
{
public partial class Added_Feature_Management : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AbpFeatureValues",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
Name = table.Column<string>(maxLength: 128, nullable: false),
Value = table.Column<string>(maxLength: 128, nullable: false),
ProviderName = table.Column<string>(maxLength: 64, nullable: true),
ProviderKey = table.Column<string>(maxLength: 64, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatureValues", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey",
table: "AbpFeatureValues",
columns: new[] { "Name", "ProviderName", "ProviderKey" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AbpFeatureValues");
}
}
}

116
samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/AuthServerDbContextModelSnapshot.cs

@ -266,6 +266,37 @@ namespace AuthServer.Host.Migrations
b.ToTable("AbpEntityPropertyChanges");
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(128)")
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<string>("ProviderName")
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<string>("Value")
.IsRequired()
.HasColumnType("nvarchar(128)")
.HasMaxLength(128);
b.HasKey("Id");
b.HasIndex("Name", "ProviderName", "ProviderKey");
b.ToTable("AbpFeatureValues");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b =>
{
b.Property<Guid>("Id")
@ -1379,6 +1410,82 @@ namespace AuthServer.Host.Migrations
b.ToTable("AbpSettings");
});
modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreationTime")
.HasColumnName("CreationTime")
.HasColumnType("datetime2");
b.Property<Guid?>("CreatorId")
.HasColumnName("CreatorId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("DeleterId")
.HasColumnName("DeleterId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime?>("DeletionTime")
.HasColumnName("DeletionTime")
.HasColumnType("datetime2");
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnName("IsDeleted")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime")
.HasColumnType("datetime2");
b.Property<Guid?>("LastModifierId")
.HasColumnName("LastModifierId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.HasKey("Id");
b.HasIndex("Name");
b.ToTable("AbpTenants");
});
modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b =>
{
b.Property<Guid>("TenantId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Name")
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<string>("Value")
.IsRequired()
.HasColumnType("nvarchar(1024)")
.HasMaxLength(1024);
b.HasKey("TenantId", "Name");
b.ToTable("AbpTenantConnectionStrings");
});
modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b =>
{
b.HasOne("Volo.Abp.AuditLogging.AuditLog", null)
@ -1582,6 +1689,15 @@ namespace AuthServer.Host.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b =>
{
b.HasOne("Volo.Abp.TenantManagement.Tenant", null)
.WithMany("ConnectionStrings")
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}

1
samples/MicroserviceDemo/applications/AuthServer.Host/Program.cs

@ -24,6 +24,7 @@ namespace AuthServer.Host
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.WithProperty("Application", "AuthServer")
.Enrich.FromLogContext()
.WriteTo.File("Logs/logs.txt")

6
samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminApp.Host.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
@ -30,9 +30,13 @@
<ProjectReference Include="..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.HttpApi.Client\Volo.Abp.PermissionManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.HttpApi.Client\Volo.Abp.Identity.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.Web\Volo.Abp.Identity.Web.csproj" />
<ProjectReference Include="..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.HttpApi.Client\Volo.Abp.TenantManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.Web\Volo.Abp.TenantManagement.Web.csproj" />
<ProjectReference Include="..\..\..\..\modules\feature-management\src\Volo.Abp.FeatureManagement.HttpApi.Client\Volo.Abp.FeatureManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\modules\blogging\src\Volo.Blogging.Application.Contracts\Volo.Blogging.Application.Contracts.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.HttpApi.Client\ProductManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.Web\ProductManagement.Web.csproj" />
<ProjectReference Include="..\..\shared\MsDemo.Shared\MsDemo.Shared.csproj" />
</ItemGroup>
<ItemGroup>

28
samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminAppHostModule.cs

@ -7,18 +7,24 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using ProductManagement;
using StackExchange.Redis;
using Microsoft.OpenApi.Models;
using MsDemo.Shared;
using Swashbuckle.AspNetCore.Swagger;
using Volo.Abp;
using Volo.Abp.AspNetCore.Authentication.OAuth;
using Volo.Abp.AspNetCore.Mvc.Client;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.Autofac;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Http.Client.IdentityModel.Web;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Web;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.Web;
using Volo.Abp.UI.Navigation;
using Volo.Blogging;
namespace BackendAdminApp.Host
@ -30,13 +36,16 @@ namespace BackendAdminApp.Host
typeof(AbpHttpClientIdentityModelWebModule),
typeof(AbpIdentityHttpApiClientModule),
typeof(AbpIdentityWebModule),
typeof(AbpTenantManagementHttpApiClientModule),
typeof(AbpTenantManagementWebModule),
typeof(BloggingApplicationContractsModule),
typeof(AbpPermissionManagementHttpApiClientModule),
typeof(ProductManagementHttpApiClientModule),
typeof(ProductManagementWebModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule)
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpFeatureManagementHttpApiClientModule)
)]
public class BackendAdminAppHostModule : AbpModule
public class BackendAdminAppHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
@ -47,6 +56,16 @@ namespace BackendAdminApp.Host
options.Languages.Add(new LanguageInfo("en", "en", "English"));
});
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MsDemoConsts.IsMultiTenancyEnabled;
});
Configure<AbpNavigationOptions>(options =>
{
options.MenuContributors.Add(new BackendAdminAppMenuContributor(configuration));
});
context.Services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
@ -71,6 +90,7 @@ namespace BackendAdminApp.Host
options.Scope.Add("BackendAdminAppGateway");
options.Scope.Add("IdentityService");
options.Scope.Add("ProductService");
options.Scope.Add("TenantManagementService");
options.ClaimActions.MapAbpClaimTypes();
});
@ -99,6 +119,10 @@ namespace BackendAdminApp.Host
app.UseVirtualFiles();
app.UseRouting();
app.UseAuthentication();
if (MsDemoConsts.IsMultiTenancyEnabled)
{
app.UseMultiTenancy();
}
app.UseAuthorization();
app.UseAbpRequestLocalization();
app.UseSwagger();

43
samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminAppMenuContributor.cs

@ -0,0 +1,43 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.UI.Navigation;
using Volo.Abp.Users;
namespace BackendAdminApp.Host
{
public class BackendAdminAppMenuContributor : IMenuContributor
{
private readonly IConfiguration _configuration;
public BackendAdminAppMenuContributor(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task ConfigureMenuAsync(MenuConfigurationContext context)
{
if (context.Menu.Name == StandardMenus.User)
{
await ConfigureUserMenuAsync(context);
}
}
private Task ConfigureUserMenuAsync(MenuConfigurationContext context)
{
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
var identityServerUrl = _configuration["AuthServer:Authority"] ?? "";
if (currentUser.IsAuthenticated)
{
//TODO: Localize menu items
context.Menu.AddItem(new ApplicationMenuItem("Account.Manage", "Manage Your Profile", $"{identityServerUrl.EnsureEndsWith('/')}Account/Manage", icon: "fa fa-cog", order: 1000, null, "_blank"));
context.Menu.AddItem(new ApplicationMenuItem("Account.Logout", "Logout", url: "/Account/Logout", icon: "fa fa-power-off", order: int.MaxValue - 1000));
}
return Task.CompletedTask;
}
}
}

9
samples/MicroserviceDemo/applications/BackendAdminApp.Host/Controllers/AccountController.cs

@ -0,0 +1,9 @@
using Volo.Abp.AspNetCore.Mvc.Authentication;
namespace BackendAdminApp.Host.Controllers
{
public class AccountController : ChallengeAccountController
{
}
}

1
samples/MicroserviceDemo/applications/BackendAdminApp.Host/Program.cs

@ -23,6 +23,7 @@ namespace BackendAdminApp.Host
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.WithProperty("Application", "BackendAdminApp")
.Enrich.FromLogContext()
.WriteTo.File("Logs/logs.txt")

36
samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs

@ -7,12 +7,14 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Identity;
using Volo.Abp.IdentityModel;
using Volo.Abp.TenantManagement;
namespace ConsoleClientDemo
{
public class ClientDemoService : ITransientDependency
{
private readonly IIdentityUserAppService _userAppService;
private readonly ITenantAppService _tenantAppService;
private readonly IProductAppService _productAppService;
private readonly IIdentityModelAuthenticationService _authenticator;
private readonly AbpRemoteServiceOptions _remoteServiceOptions;
@ -21,10 +23,12 @@ namespace ConsoleClientDemo
IIdentityUserAppService userAppService,
IProductAppService productAppService,
IIdentityModelAuthenticationService authenticator,
IOptions<AbpRemoteServiceOptions> remoteServiceOptions)
IOptions<AbpRemoteServiceOptions> remoteServiceOptions,
ITenantAppService tenantAppService)
{
_userAppService = userAppService;
_authenticator = authenticator;
_tenantAppService = tenantAppService;
_remoteServiceOptions = remoteServiceOptions.Value;
_productAppService = productAppService;
}
@ -33,6 +37,7 @@ namespace ConsoleClientDemo
{
await TestWithHttpClient();
await TestIdentityService();
await TestTenantManagementService();
await TestProductService();
}
@ -101,6 +106,35 @@ namespace ConsoleClientDemo
}
}
/// <summary>
/// Shows how to use application service interfaces (ITenantAppService in this sample)
/// to call a remote service which is possible by the dynamic http client proxy system.
/// No need to use IIdentityModelAuthenticationService since the dynamic http client proxy
/// system internally uses it. You just inject a service (ITenantAppService)
/// and call a method (GetListAsync) like a local method.
/// </summary>
private async Task TestTenantManagementService()
{
Console.WriteLine();
Console.WriteLine("*** TestTenantManagementService ************************************");
try
{
var output = await _tenantAppService.GetListAsync(new GetTenantsInput());
Console.WriteLine("Total tenant count: " + output.TotalCount);
foreach (var tenant in output.Items)
{
Console.WriteLine($"- Id={tenant.Id}, Name={tenant.Name}");
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
/// <summary>
/// Shows how to use application service interfaces (IProductAppService in this sample)
/// to call a remote service which is possible by the dynamic http client proxy system.

1
samples/MicroserviceDemo/applications/ConsoleClientDemo/ConsoleClientDemo.csproj

@ -14,6 +14,7 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Http.Client.IdentityModel\Volo.Abp.Http.Client.IdentityModel.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.HttpApi.Client\Volo.Abp.Identity.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.HttpApi.Client\Volo.Abp.TenantManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.HttpApi.Client\ProductManagement.HttpApi.Client.csproj" />
</ItemGroup>

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

Loading…
Cancel
Save