diff --git a/docs/en/Authorization.md b/docs/en/Authorization.md
index de89920946..1bceda951f 100644
--- a/docs/en/Authorization.md
+++ b/docs/en/Authorization.md
@@ -204,6 +204,10 @@ If you define and register a policy to the ASP.NET Core authorization system wit
See [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) document to learn how to define a custom policy.
+### Changing Permission Definitions of a Depended Module
+
+A class deriving from the `PermissionDefinitionProvider` (just like the example above) can also get existing permission definitions (defined by the depended [modules](Module-Development-Basics.md)) and change their definitions.
+
## IAuthorizationService
ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization.
diff --git a/docs/en/Customizing-Application-Modules-Guide.md b/docs/en/Customizing-Application-Modules-Guide.md
index 9989c6fc09..7f913f366a 100644
--- a/docs/en/Customizing-Application-Modules-Guide.md
+++ b/docs/en/Customizing-Application-Modules-Guide.md
@@ -53,3 +53,10 @@ This section suggests some approaches if you decided to use pre-built applicatio
* [Overriding Services](Customizing-Application-Modules-Overriding-Services.md)
* [Overriding the User Interface](Customizing-Application-Modules-Overriding-User-Interface.md)
+### See Also
+
+Also, see the following documents:
+
+* See [the localization document](Localization.md) to learn how to extend existing localization resources.
+* See [the settings document](Settings.md) to learn how to change setting definitions of a depended module.
+* See [the authorization document](Authorization.md) to learn how to change permission definitions of a depended module.
\ No newline at end of file
diff --git a/docs/en/IdentityServer-Integration.md b/docs/en/IdentityServer-Integration.md
new file mode 100644
index 0000000000..da82d4bc6a
--- /dev/null
+++ b/docs/en/IdentityServer-Integration.md
@@ -0,0 +1,3 @@
+# IdentityServer Integration
+
+TODO
\ No newline at end of file
diff --git a/docs/en/Modules/Blogging.md b/docs/en/Modules/Blogging.md
index 0d2ab35c21..32d2e023ea 100644
--- a/docs/en/Modules/Blogging.md
+++ b/docs/en/Modules/Blogging.md
@@ -1,3 +1,3 @@
-# IdentityServer Module
+# Blogging Module
TODO
\ No newline at end of file
diff --git a/docs/en/Modules/Docs.md b/docs/en/Modules/Docs.md
index afcc1a9dd7..db752ceb74 100644
--- a/docs/en/Modules/Docs.md
+++ b/docs/en/Modules/Docs.md
@@ -20,23 +20,25 @@ When you use GitHub to store your docs, Docs Module supports versioning. If you
> Docs module follows the [module architecture best practices](../Best-Practices/Module-Architecture.md) guide.
+## Installation
+### 1- Download
-## Installation
+If you do not have an existing ABP project, this step shows you how to create a new project from [abp.io](https://abp.io) to add the Docs Module. If you already have an ABP project, you can skip this step.
-### 1- Download
+It is recommended to use ABP CLI to create new projects. Use the following command:
-If you do not have an existing ABP project, this step shows you how to create a new project from [abp.io](https://abp.io) to add the Docs Module. If you already have an ABP project, you can skip this step.
+`abp new Acme.MyProject`
-Navigate to https://abp.io/Templates. Enter your project name as `Acme.MyProject`, select `ASP.NET Core Mvc Application` and select `Entity Framework Core` for the database provider.
+You can also navigate to https://abp.io/get-started. Enter your project name as `Acme.MyProject`, other use default options.
-Note that this document covers `Entity Framework Core` provider but you can also select `MongoDB` as your database provider.
+Note that this document covers `Entity Framework Core` provider but you can also select `MongoDB` as your database provider.

### 2- Running The Empty Application
-After you download the project, extract the ZIP file and open `Acme.MyProject.sln`. You will see that the solution consists of `Application`, `Domain `, `EntityFrameworkCore` and `Web` projects. Right click on `Acme.MyProject.Web` project and **Set as StartUp Project**.
+After you download the project, extract the ZIP file and open `Acme.MyProject.sln`. You will see that the solution consists of `Application`, `Application.Contracts`, `DbMigrator`, `Domain`, `Domain.Shared`, `EntityFrameworkCore`, `EntityFrameworkCore.DbMigations`, `HttpApi`, `HttpApi.Client` and `Web` projects. Right click on `Acme.MyProject.Web` project and **Set as StartUp Project**.

@@ -45,16 +47,14 @@ The database connection string is located in `appsettings.json` of your `Acme.My
```json
{
"ConnectionStrings": {
- "Default": "Server=localhost;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true"
+ "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
```
+Run `Acme.MyProject.DbMigrator` project, it will be responsible for applying database migration and seed data. The database `MyProject` will be created in your database server.
-
-Open `Package Manager Console` in the Visual Studio and choose `src\Acme.MyProject.EntityFrameworkCore` as the default project. Run `Update-Database` command to create your new database. The database `MyProject` will be created in your database server.
-
-Now an empty ABP project has been created! You can now run your project and see the empty website.
+Now an empty ABP project has been created! You can now run your project and see the empty website.
To login your website enter `admin` as the username and `1q2w3E*` as the password.
@@ -62,36 +62,27 @@ To login your website enter `admin` as the username and `1q2w3E*` as the passwor
Docs module packages are hosted on NuGet. There are 4 packages that needs be to installed to your application. Each package has to be installed to the relevant project.
-* [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) needs to be referenced to `Acme.MyProject.Domain` project.
+It is recommended to use the ABP CLI to install the module, open the CMD window in the solution file (`.sln`) directory, and run the following command:
- * Edit `Acme.MyProject.Domain.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest.
+`abp add-module Volo.Docs`
- ```csharp
-
- ```
-* [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) needs to be referenced to `Acme.MyProject.EntityFrameworkCore` project.
+Or you can also manually install nuget package to each project:
- - Edit `Acme.MyProject.EntityFrameworkCore.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest.
+* Install [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Domain` project.
- ```csharp
-
- ```
-* [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) needs to be referenced to `Acme.MyProject.Application` project.
+ `Install-Package Volo.Docs.Domain`
- * Edit `Acme.MyProject.Application.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest.
+* Install [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) nuget package to `Acme.MyProject.EntityFrameworkCore` project.
- ```csharp
-
- ```
-* [Volo.Docs.Web ](https://www.nuget.org/packages/Volo.Docs.Web/)needs to be referenced to `Acme.MyProject.Web` project.
+ `Install-Package Volo.Docs.EntityFrameworkCore`
- - Edit `Acme.MyProject.Web.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest.
+* Install [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) nuget package to `Acme.MyProject.Application` project.
- ```csharp
-
- ```
+ `Install-Package Volo.Docs.Application`
+* Install [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Web` project.
+ `Install-Package Volo.Docs.Web`
### 3- Adding Module Dependencies
@@ -132,7 +123,6 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a
}
```
-
* Open `MyProjectApplicationModule.cs`and add `typeof(DocsApplicationModule)` as shown below;
```csharp
@@ -157,7 +147,6 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a
}
```
-
* Open `MyProjectWebModule.cs`and add `typeof(DocsWebModule)` as shown below;
```csharp
@@ -176,43 +165,62 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a
}
```
-
-
### 4- Database Integration
#### 4.1- Entity Framework Integration
-If you choose Entity Framework as your database provider, you need to configure the Docs Module in your DbContext. To do this;
+If you choose Entity Framework as your database provider, you need to configure the Docs Module. To do this;
-- Open `MyProjectDbContext.cs` and add `modelBuilder.ConfigureDocs()` to the `OnModelCreating()`
+- Open `MyProjectMigrationsDbContext.cs` and add `builder.ConfigureDocs()` to the `OnModelCreating()`.
```csharp
- [ConnectionStringName("Default")]
- public class MyProjectDbContext : AbpDbContext
- {
- public MyProjectDbContext(DbContextOptions options)
- : base(options)
- {
-
- }
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- //...
- modelBuilder.ConfigureDocs();
- }
- }
+ public class MyProjectMigrationsDbContext : AbpDbContext
+ {
+ public MyProjectMigrationsDbContext(DbContextOptions options)
+ : base(options)
+ {
+
+ }
+
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ base.OnModelCreating(builder);
+
+ /* Include modules to your migration db context */
+
+ builder.ConfigurePermissionManagement();
+ builder.ConfigureSettingManagement();
+ builder.ConfigureBackgroundJobs();
+ builder.ConfigureAuditLogging();
+ builder.ConfigureIdentity();
+ builder.ConfigureIdentityServer();
+ builder.ConfigureFeatureManagement();
+ builder.ConfigureTenantManagement();
+ builder.ConfigureDocs(); //Add this line to configure the Docs Module
+
+ /* Configure customizations for entities from the modules included */
+
+ builder.Entity(b =>
+ {
+ b.ConfigureCustomUserProperties();
+ });
+
+ /* Configure your own tables/entities inside the ConfigureQaDoc method */
+
+ builder.ConfigureMyProject();
+ }
+ }
```
-* Open `Package Manager Console` in `Visual Studio` and choose `Acme.MyProject.EntityFrameworkCore` as default project. Then write the below command to add the migration for Docs Module.
+* Open `Package Manager Console` in `Visual Studio` and choose `Acme.MyProject.EntityFrameworkCore.DbMigrations` as default project. Then write the below command to add the migration for Docs Module.
```csharp
add-migration Added_Docs_Module
```
- When the command successfully executes , you will see a new migration file named as `20181221111621_Added_Docs_Module` in the folder `Acme.MyProject.EntityFrameworkCore\Migrations`.
+ When the command successfully executes , you will see a new migration file named as `20181221111621_Added_Docs_Module` in the folder `Acme.MyProject.EntityFrameworkCore.DbMigrations\Migrations`.
- Now, update the database for Docs module database changes. To do this run the below code on `Package Manager Console` in `Visual Studio`. Be sure `Acme.MyProject.EntityFrameworkCore` is still default project.
+ Now, update the database for Docs module database changes. To do this run the below code on `Package Manager Console` in `Visual Studio`. Be sure `Acme.MyProject.EntityFrameworkCore.DbMigrations` is still default project.
```csharp
update-database
@@ -220,7 +228,6 @@ If you choose Entity Framework as your database provider, you need to configure
Finally, you can check your database to see the newly created tables. For example you can see `DocsProjects` table must be added to your database.
-
### 5- Linking Docs Module
The default route for Docs module is;
@@ -250,7 +257,7 @@ To add Docs module link to your application menu;
}
```
-The `Menu:Docs` keyword is a localization key. To localize the menu text, open `Localization\MyProject\en.json` in the project `Acme.MyProject.Domain`. And add the below line
+The `Menu:Docs` keyword is a localization key. To localize the menu text, open `Localization\MyProject\en.json` in the project `Acme.MyProject.Domain`. And add the below line
```json
"Menu:Docs": "Documents"
@@ -270,7 +277,7 @@ Final look of **en.json**
}
```
-The new menu item for Docs Module is added to the menu. Run your web application and browse to `http://localhost:YOUR_PORT_NUMBER/documents` URL.
+The new menu item for Docs Module is added to the menu. Run your web application and browse to `http://localhost:YOUR_PORT_NUMBER/documents` URL.
You will see a warning says;
@@ -316,23 +323,25 @@ You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documen
- ExtraProperties:
```json
- {"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***"}
+ {"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***","GitHubUserAgent":""}
```
Note that `GitHubAccessToken` is masked with `***`. It's a private token that you must get it from GitHub. See https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
-- MainWebsiteUrl: `/`
+- MainWebsiteUrl: `/`
- LatestVersionBranchName: `master`
For `SQL` databases, you can use the below `T-SQL` command to insert the specified sample into your `DocsProjects` table:
```mssql
-INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***"}', N'/', N'master', N'')
+INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***","GitHubUserAgent":""}', N'/', N'master', N'')
```
Be aware that `GitHubAccessToken` is masked. It's a private token and you must get your own token and replace the `***` string.
+Now you can run the application and navigate to `/Documents`.
+
#### Sample Project Record for "FileSystem"
You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documents to configure your GitHub document store.
@@ -561,11 +570,34 @@ The upper sample `JSON` file renders the below navigation menu as `HTML`.

+Finally a new Docs Module is added to your project which is feeded with GitHub.
-Finally a new Docs Module is added to your project which is feeded with GitHub.
+## Full-Text Search(Elastic Search)
-## Next
+The Docs module supports full-text search using Elastic Search. It is not enabled by default. You can configure `DocsElasticSearchOptions` to enable it.
+
+```
+Configure(options =>
+{
+ options.Enable = true;
+ options.IndexName = "your_index_name"; //default IndexName is abp_documents
+});
+```
+
+The `Index` is automatically created after the application starts if the `Index` does not exist.
-Docs Module is also available as a standalone application. Check out [VoloDocs](../Apps/VoloDocs).
+`DefaultElasticClientProvider` is responsible for creating `IElasticClient`. By default, it reads Elastic Search's `Url` from `IConfiguration`.
+If your `IElasticClient` needs additional configuration, please use override `IElasticClientProvider` service and replace it in the [dependency injection](Dependency-Injection.md) system.
+
+```
+{
+ "ElasticSearch": {
+ "Url": "http://localhost:9200"
+ }
+}
+```
+
+## Next
+Docs Module is also available as a standalone application. Check out [VoloDocs](../Apps/VoloDocs).
\ No newline at end of file
diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md
index 4ee77d065c..1e1e4e9f81 100644
--- a/docs/en/Multi-Tenancy.md
+++ b/docs/en/Multi-Tenancy.md
@@ -302,7 +302,7 @@ TODO:...
Volo.Abp.AspNetCore.MultiTenancy package adds following tenant resolvers to determine current tenant from current web request (ordered by priority). These resolvers are added and work out of the box:
-* **CurrentUserTenantResolveContributor**: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be stay as the first contributor for security**.
+* **CurrentUserTenantResolveContributor**: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for security**.
* **QueryStringTenantResolver**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default.
* **RouteTenantResolver**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route.
* **HeaderTenantResolver**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default.
@@ -343,8 +343,10 @@ namespace MyCompany.MyProject
{
Configure(options =>
{
- //Subdomain format: {0}.mydomain.com (adding as the highest priority resolver)
- options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));
+ //Subdomain format: {0}.mydomain.com
+ //Adding as the second highest priority resolver after 'CurrentUserTenantResolveContributor' to
+ //ensure the user cannot impersonate a different tenant.
+ options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));
});
//...
@@ -355,7 +357,7 @@ namespace MyCompany.MyProject
{0} is the the placeholder to determine current tenant's unique name.
-Instead of ``options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));`` you can use this shortcut:
+Instead of ``options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));`` you can use this shortcut:
````C#
options.AddDomainTenantResolver("{0}.mydomain.com");
diff --git a/docs/en/UI/Angular/Config-State.md b/docs/en/UI/Angular/Config-State.md
new file mode 100644
index 0000000000..72c8e6c6fd
--- /dev/null
+++ b/docs/en/UI/Angular/Config-State.md
@@ -0,0 +1,290 @@
+# Config State
+
+`ConfigStateService` is a singleton service, i.e. provided in root level of your application, and is actually a façade for interacting with application configuration state in the `Store`.
+
+## Before Use
+
+In order to use the `ConfigStateService` you must inject it in your class as a dependency.
+
+```js
+import { ConfigStateService } from '@abp/ng.core';
+
+@Component({
+ /* class metadata here */
+})
+class DemoComponent {
+ constructor(private config: ConfigStateService) {}
+}
+```
+
+You do not have to provide the `ConfigStateService` at module or component/directive level, because it is already **provided in root**.
+
+## Selector Methods
+
+`ConfigStateService` has numerous selector methods which allow you to get a specific configuration or all configurations from the `Store`.
+
+### How to Get All Configurations From the Store
+
+You can use the `getAll` method of `ConfigStateService` to get all of the configuration object from the store. It is used as follows:
+
+```js
+// this.config is instance of ConfigStateService
+
+const config = this.config.getAll();
+```
+
+### How to Get a Specific Configuration From the Store
+
+You can use the `getOne` method of `ConfigStateService` to get a specific configuration property from the store. For that, the property name should be passed to the method as parameter.
+
+```js
+// this.config is instance of ConfigStateService
+
+const currentUser = this.config.getOne("currentUser");
+```
+
+On occasion, you will probably want to be more specific than getting just the current user. For example, here is how you can get the `tenantId`:
+
+```js
+const tenantId = this.config.getDeep("currentUser.tenantId");
+```
+
+or by giving an array of keys as parameter:
+
+```js
+const tenantId = this.config.getDeep(["currentUser", "tenantId"]);
+```
+
+FYI, `getDeep` is able to do everything `getOne` does. Just keep in mind that `getOne` is slightly faster.
+
+#### Config State Properties
+
+Please refer to `Config.State` type for all the properties you can get with `getOne` and `getDeep`. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L7).
+
+### How to Get the Application Information From the Store
+
+The `getApplicationInfo` method is used to get the application information from the environment variables stored as the config state. This is how you can use it:
+
+```js
+// this.config is instance of ConfigStateService
+
+const appInfo = this.config.getApplicationInfo();
+```
+
+This method never returns `undefined` or `null` and returns an empty object literal (`{}`) instead. In other words, you will never get an error when referring to the properties of `appInfo` above.
+
+#### Application Information Properties
+
+Please refer to `Config.Application` type for all the properties you can get with `getApplicationInfo`. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L21).
+
+### How to Get API URL From the Store
+
+The `getApplicationInfo` method is used to get a specific API URL from the environment variables stored as the config state. This is how you can use it:
+
+```js
+// this.config is instance of ConfigStateService
+
+const apiUrl = this.config.getApiUrl();
+// environment.apis.default.url
+
+const searchUrl = this.config.getApiUrl("search");
+// environment.apis.search.url
+```
+
+This method returns the `url` of a specific API based on the key given as its only parameter. If there is no key, `'default'` is used.
+
+### How to Get All Settings From the Store
+
+You can use the `getSettings` method of `ConfigStateService` to get all of the settings object from the configuration state. Here is how you get all settings:
+
+```js
+// this.config is instance of ConfigStateService
+
+const settings = this.config.getSettings();
+```
+
+In addition, the method lets you search settings by **passing a keyword** to it.
+
+```js
+const localizationSettings = this.config.getSettings("Localization");
+/*
+{
+ 'Abp.Localization.DefaultLanguage': 'en'
+}
+*/
+```
+
+Beware though, **settings search is case sensitive**.
+
+### How to Get a Specific Setting From the Store
+
+You can use the `getSetting` method of `ConfigStateService` to get a specific setting from the configuration state. Here is an example:
+
+```js
+// this.config is instance of ConfigStateService
+
+const defaultLang = this.config.getSetting("Abp.Localization.DefaultLanguage");
+// 'en'
+```
+
+### How to Get a Specific Permission From the Store
+
+You can use the `getGrantedPolicy` method of `ConfigStateService` to get a specific permission from the configuration state. For that, you should pass a policy key as parameter to the method.
+
+```js
+// this.config is instance of ConfigStateService
+
+const hasIdentityPermission = this.config.getGrantedPolicy("Abp.Identity");
+// true
+```
+
+You may also **combine policy keys** to fine tune your selection:
+
+```js
+// this.config is instance of ConfigStateService
+
+const hasIdentityAndAccountPermission = this.config.getGrantedPolicy(
+ "Abp.Identity && Abp.Account"
+);
+// false
+
+const hasIdentityOrAccountPermission = this.config.getGrantedPolicy(
+ "Abp.Identity || Abp.Account"
+);
+// true
+```
+
+Please consider the following **rules** when creating your permission selectors:
+
+- Maximum 2 keys can be combined.
+- `&&` operator looks for both keys.
+- `||` operator looks for either key.
+- Empty string `''` as key will return `true`
+- Using an operator without a second key will return `false`
+
+### How to Get Translations From the Store
+
+The `getLocalization` method of `ConfigStateService` is used for translations. Here are some examples:
+
+```js
+// this.config is instance of ConfigStateService
+
+const identity = this.config.getLocalization("AbpIdentity::Identity");
+// 'identity'
+
+const notFound = this.config.getLocalization("AbpIdentity::IDENTITY");
+// 'AbpIdentity::IDENTITY'
+
+const defaultValue = this.config.getLocalization({
+ key: "AbpIdentity::IDENTITY",
+ defaultValue: "IDENTITY"
+});
+// 'IDENTITY'
+```
+
+Please check out the [localization documentation](./Localization.md) for details.
+
+## Dispatch Methods
+
+`ConfigStateService` has several dispatch methods which allow you to conveniently dispatch predefined actions to the `Store`.
+
+### How to Get Application Configuration From Server
+
+The `dispatchGetAppConfiguration` triggers a request to an endpoint that responds with the application state and then places this response to the `Store` as configuration state.
+
+```js
+// this.config is instance of ConfigStateService
+
+this.config.dispatchGetAppConfiguration();
+// returns a state stream which emits after dispatch action is complete
+```
+
+Note that **you do not have to call this method at application initiation**, because the application configuration is already being received from the server at start.
+
+### How to Patch Route Configuration
+
+The `dispatchPatchRouteByName` finds a route by its name and replaces its configuration in the `Store` with the new configuration passed as the second parameter.
+
+```js
+// this.config is instance of ConfigStateService
+
+const newRouteConfig: Partial = {
+ name: "Home",
+ path: "home",
+ children: [
+ {
+ name: "Dashboard",
+ path: "dashboard"
+ }
+ ]
+};
+
+this.config.dispatchPatchRouteByName("::Menu:Home", newRouteConfig);
+// returns a state stream which emits after dispatch action is complete
+```
+
+### How to Add a New Route Configuration
+
+The `dispatchAddRoute` adds a new route to the configuration state in the `Store`. For this, the route config should be passed as the parameter of the method.
+
+```js
+// this.config is instance of ConfigStateService
+
+const newRoute: ABP.Route = {
+ name: "My New Page",
+ iconClass: "fa fa-dashboard",
+ path: "page",
+ invisible: false,
+ order: 2,
+ requiredPolicy: "MyProjectName::MyNewPage"
+};
+
+this.config.dispatchAddRoute(newRoute);
+// returns a state stream which emits after dispatch action is complete
+```
+
+The `newRoute` will be placed as at root level, i.e. without any parent routes and its url will be stored as `'/path'`.
+
+If you want **to add a child route, you can do this:**
+
+```js
+// this.config is instance of ConfigStateService
+
+const newRoute: ABP.Route = {
+ parentName: "AbpAccount::Login",
+ name: "My New Page",
+ iconClass: "fa fa-dashboard",
+ path: "page",
+ invisible: false,
+ order: 2,
+ requiredPolicy: "MyProjectName::MyNewPage"
+};
+
+this.config.dispatchAddRoute(newRoute);
+// returns a state stream which emits after dispatch action is complete
+```
+
+The `newRoute` will then be placed as a child of the parent route named `'AbpAccount::Login'` and its url will be set as `'/account/login/page'`.
+
+#### Route Configuration Properties
+
+Please refer to `ABP.Route` type for all the properties you can pass to `dispatchSetEnvironment` in its parameter. It can be found in the [common.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/common.ts#L27).
+
+### How to Set the Environment
+
+The `dispatchSetEnvironment` places environment variables passed to it in the `Store` under the configuration state. Here is how it is used:
+
+```js
+// this.config is instance of ConfigStateService
+
+this.config.dispatchSetEnvironment({
+ /* environment properties here */
+});
+// returns a state stream which emits after dispatch action is complete
+```
+
+Note that **you do not have to call this method at application initiation**, because the environment variables are already being stored at start.
+
+#### Environment Properties
+
+Please refer to `Config.Environment` type for all the properties you can pass to `dispatchSetEnvironment` as parameter. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L13).
diff --git a/docs/en/UI/AspNetCore/Bundling-Minification.md b/docs/en/UI/AspNetCore/Bundling-Minification.md
index fc4ddaced3..5af3f177d5 100644
--- a/docs/en/UI/AspNetCore/Bundling-Minification.md
+++ b/docs/en/UI/AspNetCore/Bundling-Minification.md
@@ -51,7 +51,7 @@ This bundle defines a style bundle with a **unique name**: `MyGlobalBundle`. It'
* ABP creates the bundle as **lazy** from the provided files when it's **first requested**. For the subsequent calls, it's returned from the **cache**. That means if you conditionally add the files to the bundle, it's executed only once and any changes of the condition will not effect the bundle for the next requests.
* ABP adds bundle files **individually** to the page for the `development` environment. It automatically bundles & minifies for other environments (`staging`, `production`...).
-* The bundle files may be **physical** files or [**virtual/embedded** files](../Virtual-File-System.md).
+* The bundle files may be **physical** files or [**virtual/embedded** files](../../Virtual-File-System.md).
* ABP automatically adds **version query string** to the bundle file URL to prevent browsers from caching when the bundle is being updated. (like ?_v=67872834243042 - generated from last change date of the related files). The versioning works even if the bundle files are individually added to the page (on the development environment).
### Importing The Bundling Tag Helpers
@@ -102,7 +102,7 @@ The bundle name will be *scripts.my-scripts* for the example above ("/" is repla
## Bundling Options
-If you need to use same bundle in **multiple pages** or want to use some more **powerful features**, you can configure bundles **by code** in your [module](../Module-Development-Basics.md) class.
+If you need to use same bundle in **multiple pages** or want to use some more **powerful features**, you can configure bundles **by code** in your [module](../../Module-Development-Basics.md) class.
### Creating A New Bundle
@@ -143,7 +143,7 @@ This time, no file defined in the tag helper definition because the bundle files
### Configuring An Existing Bundle
-ABP supports [modularity](../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a depended module. Example:
+ABP supports [modularity](../../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a depended module. Example:
````C#
[DependsOn(typeof(MyWebModule))]
@@ -159,7 +159,7 @@ public class MyWebExtensionModule : AbpModule
bundle.AddFiles(
"/scripts/my-extension-script.js"
);
- });
+ });
});
}
}
@@ -195,7 +195,7 @@ services.Configure(options =>
.ScriptBundles
.Configure("MyGlobalBundle", bundle => {
bundle.AddContributors(typeof(MyExtensionGlobalStyleContributor));
- });
+ });
});
````
@@ -327,7 +327,7 @@ services.Configure(options =>
.AddFiles(
"/styles/mytheme-global-styles.css"
);
- });
+ });
});
````
diff --git a/docs/en/UI/AspNetCore/Customization-User-Interface.md b/docs/en/UI/AspNetCore/Customization-User-Interface.md
index 7e49efd602..c5c974e5eb 100644
--- a/docs/en/UI/AspNetCore/Customization-User-Interface.md
+++ b/docs/en/UI/AspNetCore/Customization-User-Interface.md
@@ -9,7 +9,7 @@ This section covers the [Razor Pages](https://docs.microsoft.com/en-us/aspnet/co
You typically have three kind of override requirement for a page:
* Overriding **only the Page Model** (C#) side to perform additional logic without changing the page UI.
-* Overring **only the Razor Page** (.chtml file) to change the UI without changing the c# behind the page.
+* Overriding **only the Razor Page** (.chtml file) to change the UI without changing the c# behind the page.
* **Completely overriding** the page.
### Overriding a Page Model (C#)
@@ -158,7 +158,7 @@ If you need, you can also replace [the code behind c# class](https://github.com/
Just as explained above, you can replace any component, layout or c# class of the used theme. See the [theming document](Theming.md) for more information on the theming system.
-### Overriding Static Resources
+## Overriding Static Resources
Overriding a static embedded resource (like JavaScript, Css or image files) of a module is pretty easy. Just place a file in the same path in your solution and let the Virtual File System to handle it.
@@ -437,4 +437,34 @@ See the layouts section below to learn more about the layout system.
### Layouts
-TODO
\ No newline at end of file
+Layout system allows themes to define standard, named layouts and allows any page to select a proper layout for its purpose. There are three pre-defined layouts:
+
+* "**Application**": The main (and the default) layout for an application. It typically contains header, menu (sidebar), footer, toolbar... etc.
+* "**Account**": This layout is used by login, register and other similar pages. It is used for the pages under the `/Pages/Account` folder by default.
+* "**Empty**": Empty and minimal layout.
+
+These names are defined in the `StandardLayouts` class as constants. You can definitely create your own layouts, but these are standard layout names and implemented by all the themes out of the box.
+
+#### Layout Location
+
+You can find the layout files [here](https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts) for the basic theme. You can take them as references to build your own layouts or you can override them if necessary.
+
+#### ITheme
+
+ABP Framework uses the `ITheme` service to get the layout location by the layout name. You can replace this service to dynamically select the layout location.
+
+#### IThemeManager
+
+`IThemeManager` is used to obtain the current theme and get the layout path. Any page can determine the layout of its own. Example:
+
+````html
+@using Volo.Abp.AspNetCore.Mvc.UI.Theming
+@inject IThemeManager ThemeManager
+@{
+ Layout = ThemeManager.CurrentTheme.GetLayout(StandardLayouts.Empty);
+}
+````
+
+This page will use the empty layout. You use `ThemeManager.CurrentTheme.GetEmptyLayout();` extension method as a shortcut.
+
+If you want to set the layout for all the pages under a specific folder, then write the code above in a `_ViewStart.cshtml` file under that folder.
diff --git a/docs/en/UI/AspNetCore/Widgets.md b/docs/en/UI/AspNetCore/Widgets.md
index a45056ba68..062fefb078 100644
--- a/docs/en/UI/AspNetCore/Widgets.md
+++ b/docs/en/UI/AspNetCore/Widgets.md
@@ -4,8 +4,8 @@ ABP provides a model and infrastructure to create **reusable widgets**. Widget s
* Have **scripts & styles** dependencies for your widget.
* Create **dashboards** with widgets used inside.
-* Define widgets in reusable **[modules](../Module-Development-Basics.md)**.
-* Co-operate widgets with **[authorization](../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems.
+* Define widgets in reusable **[modules](../../Module-Development-Basics.md)**.
+* Co-operate widgets with **[authorization](../../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems.
## Basic Widget Definition
@@ -13,7 +13,7 @@ ABP provides a model and infrastructure to create **reusable widgets**. Widget s
As the first step, create a new regular ASP.NET Core View Component:
-
+
**MySimpleWidgetViewComponent.cs**:
@@ -184,7 +184,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
-See [the localization document](../Localization.md) to learn about localization resources and keys.
+See [the localization document](../../Localization.md) to learn about localization resources and keys.
## Style & Script Dependencies
@@ -220,7 +220,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
-ABP takes account these dependencies and properly adds to the view/page when you use the widget. Style/script files can be **physical or virtual**. It is completely integrated to the [Virtual File System](../Virtual-File-System.md).
+ABP takes account these dependencies and properly adds to the view/page when you use the widget. Style/script files can be **physical or virtual**. It is completely integrated to the [Virtual File System](../../Virtual-File-System.md).
### Defining Bundle Contributors
@@ -454,7 +454,7 @@ Used to refresh the widget when needed. It has a filter argument that can be use
Some widgets may need to be available only for authenticated or authorized users. In this case, use the following properties of the `Widget` attribute:
* `RequiresAuthentication` (`bool`): Set to true to make this widget usable only for authentication users (user have logged in to the application).
-* `RequiredPolicies` (`List`): A list of policy names to authorize the user. See [the authorization document](../Authorization.md) for more info about policies.
+* `RequiredPolicies` (`List`): A list of policy names to authorize the user. See [the authorization document](../../Authorization.md) for more info about policies.
Example:
@@ -487,7 +487,7 @@ Configure(options =>
});
```
-Write this into the `ConfigureServices` method of your [module](../Module-Development-Basics.md). All the configuration done with the `Widget` attribute is also possible with the `AbpWidgetOptions`. Example configuration that adds a style for the widget:
+Write this into the `ConfigureServices` method of your [module](../../Module-Development-Basics.md). All the configuration done with the `Widget` attribute is also possible with the `AbpWidgetOptions`. Example configuration that adds a style for the widget:
````csharp
Configure(options =>
diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json
index 1491a1bcb9..c273e59346 100644
--- a/docs/en/docs-nav.json
+++ b/docs/en/docs-nav.json
@@ -316,6 +316,10 @@
"text": "Permission Management",
"path": "UI/Angular/Permission-Management.md"
},
+ {
+ "text": "Config State",
+ "path": "UI/Angular/Config-State.md"
+ },
{
"text": "Component Replacement",
"path": "UI/Angular/Component-Replacement.md"
diff --git a/docs/en/images/docs-module_download-new-abp-project.png b/docs/en/images/docs-module_download-new-abp-project.png
index 0da3b7a67a..6424ad7622 100644
Binary files a/docs/en/images/docs-module_download-new-abp-project.png and b/docs/en/images/docs-module_download-new-abp-project.png differ
diff --git a/docs/en/images/docs-module_solution-explorer.png b/docs/en/images/docs-module_solution-explorer.png
index cafc38f0b0..2988ec4134 100644
Binary files a/docs/en/images/docs-module_solution-explorer.png and b/docs/en/images/docs-module_solution-explorer.png differ
diff --git a/docs/zh-Hans/Modules/Docs.md b/docs/zh-Hans/Modules/Docs.md
index f2a92334fb..20382d24f2 100644
--- a/docs/zh-Hans/Modules/Docs.md
+++ b/docs/zh-Hans/Modules/Docs.md
@@ -26,15 +26,19 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块.
如果你没有现有的ABP项目, 这个步骤向你展示如何在[abp.io](https://abp.io)创建一个新项目并添加文档模块. 如果你本地已经有了一个ABP项目, 那么你可以跳过这一步.
-打开 https://abp.io/Templates. 输入项目名称为 `Acme.MyProject`, 选择 `ASP.NET Core Mvc Application` 和选择 `Entity Framework Core` 做为数据库提供者.
+推荐使用ABP CLI创建新项目,使用以下命令行:
-请注意,本文档包含了 `Entity Framework Core` 提供者 不过你也可以选择 `MongoDB` 做为数据库提供者.
+`abp new Acme.MyProject`
+
+你也可以在浏览器中导航到 https://abp.io/get-started. 输入项目名称为 `Acme.MyProject`, 其它保持默认选项.
+
+请注意,本文档包含了 `Entity Framework Core` 提供者 不过你也可以选择 `MongoDB` 做为数据库提供者.

### 2- 运行这个空项目
-下载项目后, 解压压缩文档并且打开 `Acme.MyProject.sln`. 你可以看到这个解决方案包含了 `Application`, `Domain`, `EntityFrameworkCore` 和 `Web` 项目. 右键选择 `Acme.MyProject.Web` 项目**设置为启动项目**.
+下载项目后, 解压压缩文档并且打开 `Acme.MyProject.sln`. 你可以看到这个解决方案包含了 `Application`, `Application.Contrawcts`, `DbMigrator`, `Domain`, `Domain.Shared`, `EntityFrameworkCore`, `EntityFrameworkCore.DbMigations`, `HttpApi`, `HttpApi.Client` 和 `Web` 项目. 右键选择 `Acme.MyProject.Web` 项目**设置为启动项目**.

@@ -43,12 +47,12 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块.
```json
{
"ConnectionStrings": {
- "Default": "Server=localhost;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true"
+ "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
```
-打开Visual Studio包管理控制台选择`src\Acme.MyProject.EntityFrameworkCore` 做为默认项目. 运行 `Update-Database` 命令创建数据库. 数据库`MyProject`将在数据库服务器中创建.
+运行 `Acme.MyProject.DbMigrator` 项目,它会负责应用迁移与初始化种子数据. 数据库`MyProject`将在数据库服务器中创建.
现在一个空的ABP项目已经创建完成! 现在你可以运行项目并且查看网站.
@@ -56,39 +60,28 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块.
### 2- 引用文档模块包
-文档模块包托管在Nuget上面. 需要有四个包安装到你的应用程序中. 每个包必须安装到相关的项目.
-
-* [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) 需要安装到 `Acme.MyProject.Domain` 项目.
+文档模块包托管在Nuget上面. 需要有四个包安装到你的应用程序中. 每个包必须安装到相关的项目.
- * 修改 `Acme.MyProject.Domain.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本.
+建议使用ABP CLI安装模块,在解决方案文件 (`.sln`) 目录打开 `CMD` 窗口,运行以下命令:
- ```csharp
-
- ```
+`abp add-module Volo.Docs`
-* [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) 需要安装到 `Acme.MyProject.EntityFrameworkCore` 项目.
+或者你也可以手动安装nuget包到每个项目:
- * 修改 `Acme.MyProject.EntityFrameworkCore.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本.
+* 安装[Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget包到 `Acme.MyProject.Domain` 项目.
- ```csharp
-
- ```
+ `Install-Package Volo.Docs.Domain`
-* [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) 需要安装到 `Acme.MyProject.Application` 项目.
+* 安装[Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) nuget包到 `Acme.MyProject.EntityFrameworkCore` 项目.
- * 修改 `Acme.MyProject.Application.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本.
+ `Install-Package Volo.Docs.EntityFrameworkCore`
- ```csharp
-
- ```
+* 安装[Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) nuget包到 `Acme.MyProject.Application` 项目.
-* [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Web/) 需要安装到 `Acme.MyProject.Web` 项目.
+ `Install-Package Volo.Docs.Application`
- * 修改 `Acme.MyProject.Web.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本.
-
- ```csharp
-
- ```
+* 安装[Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget包到 `Acme.MyProject.Web` 项目.
+ `Install-Package Volo.Docs.Web`
### 3- 添加模块依赖
@@ -176,37 +169,58 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块.
#### 4.1- Entity Framework 集成
-如果你选择了Entity Framework 做为数据库供应者,你需要在DbContext中配置文档模块. 做以下操作;
+如果你选择了Entity Framework 做为数据库供应者,你需要配置文档模块. 做以下操作;
-* 打开 `MyProjectDbContext.cs` 并且添加 `modelBuilder.ConfigureDocs()` 到 `OnModelCreating()` 方法中
+* 打开 `MyProjectMigrationsDbContext.cs` 并且添加 `builder.ConfigureDocs()` 到 `OnModelCreating()` 方法中
```csharp
- [ConnectionStringName("Default")]
- public class MyProjectDbContext : AbpDbContext
- {
- public MyProjectDbContext(DbContextOptions options)
- : base(options)
- {
-
- }
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- //...
- modelBuilder.ConfigureDocs();
- }
- }
+ public class MyProjectMigrationsDbContext : AbpDbContext
+ {
+ public MyProjectMigrationsDbContext(DbContextOptions options)
+ : base(options)
+ {
+
+ }
+
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ base.OnModelCreating(builder);
+
+ /* Include modules to your migration db context */
+
+ builder.ConfigurePermissionManagement();
+ builder.ConfigureSettingManagement();
+ builder.ConfigureBackgroundJobs();
+ builder.ConfigureAuditLogging();
+ builder.ConfigureIdentity();
+ builder.ConfigureIdentityServer();
+ builder.ConfigureFeatureManagement();
+ builder.ConfigureTenantManagement();
+ builder.ConfigureDocs(); //Add this line to configure the Docs Module
+
+ /* Configure customizations for entities from the modules included */
+
+ builder.Entity(b =>
+ {
+ b.ConfigureCustomUserProperties();
+ });
+
+ /* Configure your own tables/entities inside the ConfigureQaDoc method */
+
+ builder.ConfigureMyProject();
+ }
+ }
```
-* 打开 `Visual Studio` 的 `包管理控制台` 选择 `Acme.MyProject.EntityFrameworkCore` 做为默认项目. 然后编写以下命令为文档模块添加迁移.
+* 打开 `Visual Studio` 的 `包管理控制台` 选择 `Acme.MyProject.EntityFrameworkCore.DbMigrations` 做为默认项目. 然后编写以下命令为文档模块添加迁移.
```csharp
add-migration Added_Docs_Module
```
- 当命令执行成功后 , 你会看到`Acme.MyProject.EntityFrameworkCore\Migrations` 目录下有名为 `20181221111621_Added_Docs_Module` 的迁移文件.
+ 当命令执行成功后 , 你会看到`Acme.MyProject.EntityFrameworkCore.DbMigrations\Migrations` 目录下有名为 `20181221111621_Added_Docs_Module` 的迁移文件.
- 现在更新数据库. 在 `Visual Studio` 的 `包管理控制台` 中执行以下代码. 要确认已 `Acme.MyProject.EntityFrameworkCore` 项目设置为默认项目.
+ 现在更新数据库. 在 `Visual Studio` 的 `包管理控制台` 中执行以下代码. 要确认已 `Acme.MyProject.EntityFrameworkCore.DbMigrations` 项目设置为默认项目.
```csharp
update-database
@@ -309,7 +323,7 @@ There are no projects yet!
- ExtraProperties:
```json
- {"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/zh-Hans/","GitHubAccessToken":"***"}
+ {"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/zh-Hans/","GitHubAccessToken":"***","GitHubUserAgent":""}
```
注意 `GitHubAccessToken` 用 `***` 掩盖. 这是一个私人令牌,你必须从GitHub获取它. 请参阅 https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
@@ -321,11 +335,13 @@ There are no projects yet!
对于 `SQL` 数据库,你可以使用下面的 `T-SQL` 命令将指定的示例插入到 `DocsProjects` 表中:
```mssql
-INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***"}', N'/', N'master', N'')
+INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName], [ParametersDocumentName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***","GitHubUserAgent":""}', N'/', N'master', N'')
```
请注意,`GitHubAccessToken` 被屏蔽了.它是一个私人令牌,你必须获得自己的令牌并替换 `***` 字符串.
+现在你可以运行应用程序并导航到 `/Documents`.
+
#### "FileSystem" 项目的示例记录
你可以使用 [ABP Framework](https://github.com/abpframework/abp/) GitHub文档来配置你的文件系统存储.
@@ -556,6 +572,30 @@ This document assumes that you prefer to use **{{ UI_Value }}** as the UI framew
最后,为您的项目添加了一个新的Docs模块, 该模块由GitHub提供.
+## 全文搜索(Elastic Search)
+
+文档模块支持使用Elastic Search对内容进行全文搜索. 默认没有启用, 你可以配置`DocsElasticSearchOptions`启用它.
+
+```
+Configure(options =>
+{
+ options.Enable = true;
+ options.IndexName = "your_index_name"; //default IndexName is abp_documents
+});
+```
+
+应用程序启动后如果`Index`不存在则会自动创建`Index`.
+
+`DefaultElasticClientProvider`负责创建`IElasticClient`, 默认情况下它会从`IConfiguration`中读取Elastic Search的`Url`.
+如果你的IElasticClient需要其它配置请使用重写IElasticClientProvider服务并在依赖注入系统中替换它.
+```
+{
+ "ElasticSearch": {
+ "Url": "http://localhost:9200"
+ }
+}
+```
+
## 下一步
文档模块也可以做为独立的应用程序. 查看 [VoloDocs](../Apps/VoloDocs).
\ No newline at end of file
diff --git a/docs/zh-Hans/Multi-Tenancy.md b/docs/zh-Hans/Multi-Tenancy.md
index 1cfa2996d2..8913c27ede 100644
--- a/docs/zh-Hans/Multi-Tenancy.md
+++ b/docs/zh-Hans/Multi-Tenancy.md
@@ -343,8 +343,8 @@ namespace MyCompany.MyProject
{
Configure(options =>
{
- //子域名格式: {0}.mydomain.com (作为最高优先级解析器添加)
- options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));
+ //子域名格式: {0}.mydomain.com (作为第二优先级解析器添加, 位于CurrentUserTenantResolveContributor之后)
+ options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));
});
//...
@@ -355,7 +355,7 @@ namespace MyCompany.MyProject
{0}是用来确定当前租户唯一名称的占位符.
-你可以使用下面的方法,代替``options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));``:
+你可以使用下面的方法,代替``options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));``:
````C#
options.AddDomainTenantResolver("{0}.mydomain.com");
diff --git a/docs/zh-Hans/Samples/Microservice-Demo.md b/docs/zh-Hans/Samples/Microservice-Demo.md
index a96571c881..9b559797f7 100644
--- a/docs/zh-Hans/Samples/Microservice-Demo.md
+++ b/docs/zh-Hans/Samples/Microservice-Demo.md
@@ -24,82 +24,15 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服
下图展示了该系统:
-
+
### 源码
你可以从[GitHub仓库](https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo)获取源码.
-### 状态
-
-该示例的初始版本已完成.其他改进仍在开发中.
-
## 运行解决方案
-您可以从 **源代码** 或者预先配置好的 **docker-compose** 文件运行.
-
-### 使用Docker容器
-
-#### 预先要求
-
-由于所有依赖项都已预先配置, 因此作为Docker容器运行更容易. 你只需要安装[最新的docker](https://docs.docker.com/compose/install/).
-
-#### 运行容器
-
-- 克隆或下载 [ABP仓库](https://github.com/abpframework/abp).
-
-- 在存储库的`samples/MicroserviceDemo`文件夹中打开命令行.
-
-- 从Docker Hub中拉取image:
-
- ```
- docker-compose -f docker-compose.yml -f docker-compose.migrations.yml pull
- ```
-
-- 如果要在本地构建映像, 可以跳过上述步骤, 使用build命令:
-
- ```
- docker-compose -f docker-compose.yml -f docker-compose.migrations.yml build
- ```
-
- 根据你的电脑配置, 构建image可能需要**很长时间**.
-
-- 还原 SQL Server 数据库:
-
- ```
- docker-compose -f docker-compose.yml -f docker-compose.migrations.yml run restore-database
- ```
-
-- 启动容器:
-
- ```
- docker-compose up -d
- ```
-
-- 将此行添加到`hosts`文件的末尾:
-
- ```
- 127.0.0.1 auth-server
- ```
-
- hosts文件位于Windows上的`C:\Windows\System32\Drivers\etc\hosts`文件夹, Linux/MacOS的`/etc/hosts`中.
-
-#### 运行应用程序
-
-你可能想要了解容器中运行一些应用程序:
-
-* 后端管理应用程序 (BackendAdminApp.Host): `http://localhost:51512`
- *(用于管理系统中的用户和产品)*
-* 公共网站 (PublicWebsite.Host): `http://localhost:51513`
- *(用于列出产品并运行/管理博客模块)*
-* 认证服务器 (AuthServer.Host): `http://auth-server:51511/`
- *(用作使用IdentityServer4构建的单点登录和身份验证服务器)*
-* Kibana UI: `http://localhost:51510`
- *(用于显示/跟踪所有服务/应用程序/网关写入的日志)*
-
-### 从源代码运行
-
-#### 预先要求
+### 预先要求
为了能够从源代码运行解决方案, 应在你的计算机上安装并运行以下工具:
@@ -110,19 +43,19 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服
* [ElasticSearch](https://www.elastic.co/downloads/elasticsearch) 6.6+
* [Kibana](https://www.elastic.co/downloads/kibana) 6.6+ (可选,建议显示日志)
-#### 打开并构建Visual Studio解决方案
+### 打开并构建Visual Studio解决方案
* 在Visual Studio 2017 (15.9.0+)中打开`samples\MicroserviceDemo\MicroserviceDemo.sln`.
* 在`samples\MicroserviceDemo`文件夹中的命令行运行`dotnet restore`命令.
* 在Visual Studio中构建解决方案.
-#### 创建数据库
+### 创建数据库
MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库的结构。其实你可以很轻松的创建数据库,因为这个解决方案配置了使用 Entity Core Code First 来做迁移。
这个解决方案中有两个 SQL server 数据库。
-##### MsDemo_Identity 数据库
+#### MsDemo_Identity 数据库
* 右键 `AuthServer.Host` 项目,然后点击 `设置为启动项目`.
* 打开 **程序包管理器控制台** (工具 -> NuGet 包管理器 -> 程序包管理器控制台)
@@ -131,7 +64,7 @@ MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库

-##### MsDemo_ProductManagement
+#### MsDemo_ProductManagement
* 右键 `ProductService.Host` 项目,然后点击 `设置为启动项目`.
* 打开 **程序包管理器控制台** (工具 -> NuGet 包管理器 -> 程序包管理器控制台)
@@ -140,12 +73,13 @@ MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库

-#### 运行项目
+### 运行项目
按以下顺序运行项目(右键单击每个项目设置为启动项目,按Ctrl+F5运行,无需调试):
* AuthServer.Host
* IdentityService.Host
+* TenantManagementService.Host
* BloggingService.Host
* ProductService.Host
* InternalGateway.Host
@@ -158,7 +92,7 @@ MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库
Visual Studio解决方案由多个项目组成,每个项目在系统中具有不同的角色:
-
+
### 应用程序(Applications)
@@ -182,6 +116,7 @@ Visual Studio解决方案由多个项目组成,每个项目在系统中具有不
微服务没有UI,但暴露了一些REST API.
- **IdentityService.Host**: 托管用于管理用户和角色的ABP Identity模块. 它没有其他服务,仅托管Identity模块的API.
+- **TenantManagementService.Host**: 托管用于管理角色的ABP租户管理模块. 它没有其他服务,仅托管租户管理模块的API.
- **BloggingService.Host**: 托管ABP博客模块,该模块用于管理博客和帖子(典型的博客应用程序). 它没有其他服务,仅托管Blogging模块的API.
- **ProductService.Host**: 托管用于管理产品的产品模块(位于解决方案内). 它还包含用于创建/更新产品管理数据库架构的EF Core迁移.
@@ -193,7 +128,7 @@ Visual Studio解决方案由多个项目组成,每个项目在系统中具有不
此解决方案使用多个数据库:
-* **MsDemo_Identity**: 一个SQL数据库. 默认使用**SQL Server**,但可以是EF Core支持的任何DBMS. 由AuthServer和IdentityService共享. 审计日志,权限和设置也存储在此数据库中(虽然它们可以轻松拥有自己的数据库,共享相同的数据库以保持简单).
+* **MsDemo_Identity**: 一个SQL数据库. 默认使用**SQL Server**,但可以是EF Core支持的任何DBMS. 由AuthServer,IdentityService和TenantManagementService共享. 审计日志,权限和设置也存储在此数据库中(虽然它们可以轻松拥有自己的数据库,共享相同的数据库以保持简单).
* **MsDemo_ProductManagement**: 一个SQL数据库. 同样默认使用 **SQL Server**,但可以是EF Core支持的任何DBMS. 由ProductService用作专用数据库.
* **MsDemo_Blogging**: **MongoDB**数据库. 由BloggingService使用.
* **Elasticsearch**: 用于在Serilog上写日志.
@@ -1474,3 +1409,7 @@ ABP提供自动审计日志记录,详细保存每个请求(当前用户,浏览
所有服务和应用程序都配置为编写审核日志. 审核日志将保存到MsDemo_Identity SQL数据库中. 因此,您可以从单个点查询所有应用程序的所有审核日志.
审核日志记录具有`CorrelationId`属性,可用于跟踪请求. 当服务在单个Web请求中调用另一个服务时,它们都会使用相同的`CorrelationId`保存审核日志. 请参阅数据库中的`AbpAuditLogs`表.
+
+### 多租户
+
+该解决方案已配置提供[多租户](../Multi-Tenancy.md)系统,其中每个租户可以拥有其隔离的用户,角色,权限和其他数据.
\ No newline at end of file
diff --git a/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md b/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md
index b7c8c0a1b1..c0f999f996 100644
--- a/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md
+++ b/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md
@@ -52,7 +52,7 @@ namespace MyCompany.MyProject
* 当首次请求时,ABP从提供的文件中 **(延迟)lazy** 创建. 后续将从 **缓存** 中返回内容. 这意味着如果你有条件地将文件添加到包中,它只执行一次, 并且条件的任何更改都不会影响下一个请求的包.
* 在`development`环境中ABP会将包文件**单独**添加到页面中, 其他环境(`staging`,`production`...)会自动捆绑和压缩.
-* 捆绑文件可以是**物理**文件或[**虚拟/嵌入**](../Virtual-File-System.md)的文件.
+* 捆绑文件可以是**物理**文件或[**虚拟/嵌入**](../../Virtual-File-System.md)的文件.
* ABP自动将 **版本查询字符串(version query string)** 添加到捆绑文件的URL中,以防止浏览器缓存. 如:?_v=67872834243042(从文件的上次更改日期生成). 即使捆绑文件单独添加到页面(在`development`环境中), 版本控制仍然有效.
#### 导入 Bundling Tag Helpers
@@ -103,7 +103,7 @@ namespace MyCompany.MyProject
### Bundling 选项
-如果你需要在 **多个页面中使用相同的包** 或想要使用更多 **强大功能**, 你可以在[模块](../Module-Development-Basics.md)类中进行**配置**.
+如果你需要在 **多个页面中使用相同的包** 或想要使用更多 **强大功能**, 你可以在[模块](../../Module-Development-Basics.md)类中进行**配置**.
#### 创建一个新的捆绑包
@@ -144,7 +144,7 @@ public class MyWebModule : AbpModule
#### 配置现有的 Bundle
-ABP也支持[模块化](../Module-Development-Basics.md)捆绑. 模块可以修改由依赖模块创建的捆绑包.
+ABP也支持[模块化](../../Module-Development-Basics.md)捆绑. 模块可以修改由依赖模块创建的捆绑包.
例如:
````C#
diff --git a/docs/zh-Hans/UI/AspNetCore/Layout-Hooks.md b/docs/zh-Hans/UI/AspNetCore/Layout-Hooks.md
new file mode 100644
index 0000000000..49b334c6c0
--- /dev/null
+++ b/docs/zh-Hans/UI/AspNetCore/Layout-Hooks.md
@@ -0,0 +1,3 @@
+# Layout Hooks
+
+TODO
\ No newline at end of file
diff --git a/docs/zh-Hans/UI/AspNetCore/Navigation-Menu.md b/docs/zh-Hans/UI/AspNetCore/Navigation-Menu.md
new file mode 100644
index 0000000000..4e0164ec72
--- /dev/null
+++ b/docs/zh-Hans/UI/AspNetCore/Navigation-Menu.md
@@ -0,0 +1,3 @@
+# Navigation Menu
+
+TODO
\ No newline at end of file
diff --git a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Buttons.md b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Buttons.md
index 848d043312..76307ad820 100644
--- a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Buttons.md
+++ b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Buttons.md
@@ -1,30 +1,26 @@
# 按钮
-ABP框架定义了Tag Helper用于简单的创建bootstrap按钮.
+## 介绍
-``
+`abp-button` 是创建按钮的主要元素.
-## 属性
+基本用法:
-`` 有7个不同的属性.
+````xml
+Click Me
+````
-* [`button-type`](#button-type)
-* [`size`](#size)
-* [`busy-text`](#busy-text)
-* [`text`](#text)
-* [`icon`](#icon)
-* [`disabled`](#disabled)
-* [`icon-type`](#icon-type)
+## Demo
-### `button-type`
+参阅 [按钮Demo页面](https://bootstrap-taghelpers.abp.io/Components/Buttons) 查看示例.
-`button-type` 是一个可选参数. 它的默认值是 `Default`.
+## Attributes
-`Button`
+### `button-type`
-你可以为按钮选择以下按钮类型:
+指定按钮的主样式/类型. 应为以下值之一:
-* `Default`
+* `Default` (default value)
* `Primary`
* `Secondary`
* `Success`
@@ -45,11 +41,7 @@ ABP框架定义了Tag Helper用于简单的创建bootstrap按钮.
### `size`
-`size` 是一个可选参数. 它的默认值是 `Default`.
-
-`Button`
-
-你可以为按钮选择以下size类型:
+指定按钮的大小. 应为以下值之一:
* `Default`
* `Small`
@@ -62,33 +54,33 @@ ABP框架定义了Tag Helper用于简单的创建bootstrap按钮.
### `busy-text`
-`busy-text` 是一个字符串类型参数. 当按钮繁忙时设置该文本.
+当按钮busy时显示的文本.
### `text`
-`text` 是一个字符串类型参数,显示在按钮上.
-
-### `icon`
+按钮的文本. 如果你只想为为按钮设置文本,这是一种快捷方式. 例:
-`icon` 是一个字符串类型参数. 它的值取决于[`icon-type`](#`icon-type`). 默认情况下,我们对图标使用[Font Awesome](https://fontawesome.com/). 要使用它,你需要将 `icon` 参数设置为图标名称.
+````xml
+
+````
-##### 示例
+在这个示例中,你可以使用 self-closing 标签将其缩短.
-[fa-address-card](https://fontawesome.com/icons/address-card): 
-
-``
-
-> 不要忘记: 你不需要写前缀,如果你没有更改 `icon-type` ,它会为[Font Awesome](https://fontawesome.com/)图标自动添加 `fa` 前缀.
+### `icon`
-### `disabled`
+设置按钮的图标. 默认情况下它使用[Font Awesome](https://fontawesome.com/)图标库. 例:
-`disabled` 是一个布尔类型参数. 如果你设值为 `true`, 按钮会被禁用.
+````xml
+
+````
### `icon-type`
-`icon-type` 是一个可选参数。它的默认值是 `FontAwesome`. 你可以创建自己的图标类型提供程序并更改它.
+如果你不想使用font-awesome,你有两个选项:
+
+1. 设置 `icon-type` 为 `Other`,并为你的按钮编写图标样式.
+2. 如果你不使用图标,请手动使用opening和closing标签,并在标签内写任何代码.
-你可以为按钮选择以下图标类型:
+### `disabled`
-* `FontAwesome`
-* `Other`
+设置为 `true` 禁用按钮.
\ No newline at end of file
diff --git a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Index.md b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Index.md
index 1f19d50701..acbb9e872b 100644
--- a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Index.md
+++ b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Index.md
@@ -1,3 +1,26 @@
-## ABP Tag Helpers
+# ABP Tag Helpers
-"ABP tag helpers" 文档还在创建中. 你现在可以参阅[组件演示](http://bootstrap-taghelpers.abp.io/).
\ No newline at end of file
+ABP框架定义了一组**标签助手组件**. 简化开发ASP.NET Core (MVC / Razor Pages) 应用程序界面.
+
+## bootstrap 组件包装
+
+大多数标签助手是[Bootstrap](https://getbootstrap.com/) (v4+)的包装. 编写bootstrap代码并不是那么简单,其中包含太多的重复HTML标签并且也没有类型安全. ABP标签助手使其 **简单** 并且 **类型安全**.
+
+我们的目标并不是100%的包装bootstrap组件. 仍然可以编写 **原生bootstrap代码** (实际上标签助手生成的也是原生的bootstrap代码), 但我们建议尽量使用标签助手.
+
+ABP框架还向标准bootstrap组件添加了一些**实用的功能**.
+
+这里是ABP框架包装的组件列表:
+
+* [Buttons](Buttons.md)
+* ...
+
+> 在为所有的标签助手完成文档之前,你可以访问 https://bootstrap-taghelpers.abp.io/ 查看在线示例.
+
+## 表单元素
+
+参阅 [demo](https://bootstrap-taghelpers.abp.io/Components/FormElements).
+
+## 动态表单
+
+参阅 [demo](https://bootstrap-taghelpers.abp.io/Components/DynamicForms).
\ No newline at end of file
diff --git a/docs/zh-Hans/UI/AspNetCore/Theming.md b/docs/zh-Hans/UI/AspNetCore/Theming.md
index 470ef1a458..10486dd6c5 100644
--- a/docs/zh-Hans/UI/AspNetCore/Theming.md
+++ b/docs/zh-Hans/UI/AspNetCore/Theming.md
@@ -1,3 +1,3 @@
-# Theming
+# ASP.NET Core MVC / Razor Pages 主题
TODO
\ No newline at end of file
diff --git a/docs/zh-Hans/UI/AspNetCore/Toolbars.md b/docs/zh-Hans/UI/AspNetCore/Toolbars.md
new file mode 100644
index 0000000000..cc3bcd95be
--- /dev/null
+++ b/docs/zh-Hans/UI/AspNetCore/Toolbars.md
@@ -0,0 +1,3 @@
+# Toolbars
+
+TODO
\ No newline at end of file
diff --git a/docs/zh-Hans/UI/AspNetCore/Widgets.md b/docs/zh-Hans/UI/AspNetCore/Widgets.md
index 0e75a24b6f..5f86595ba2 100644
--- a/docs/zh-Hans/UI/AspNetCore/Widgets.md
+++ b/docs/zh-Hans/UI/AspNetCore/Widgets.md
@@ -2,10 +2,10 @@
ABP为创建**可重用的部件**提供了模型和基础设施. 部件系统是[ASP.NET Core ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components)的扩展. 在你有以下需求时,小部件会非常有用;
-* 在可复用的 **[模块](../Module-Development-Basics.md)** 中定义部件.
+* 在可复用的 **[模块](../../Module-Development-Basics.md)** 中定义部件.
* 在部件中引用 **scripts & styles** 脚本.
-* 使用部件创建 **[仪表盘](Dashboards.md)**.
-* 支持 **[授权](../Authorization.md)** 与 **[捆绑`bundling`](Bundling-Minification.md)** 的部件
+* 使用部件创建 **仪表盘**.
+* 支持 **[授权](../../Authorization.md)** 与 **[捆绑`bundling`](Bundling-Minification.md)** 的部件
## 基本部件定义
@@ -13,7 +13,7 @@ ABP为创建**可重用的部件**提供了模型和基础设施. 部件系统
第一部,创建一个新的ASP.NET Core View Component:
-
+
**MySimpleWidgetViewComponent.cs**:
@@ -132,7 +132,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
-参阅 [本地化文档](../Localization.md) 学习关于本地化资源的更多内容.
+参阅 [本地化文档](../../Localization.md) 学习关于本地化资源的更多内容.
## 引用 Style & Script
@@ -168,7 +168,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
-ABP会考虑到这些依赖关系, 在view/page中使用正确的方法添加部件 . 样式和script可以是物理文件也可以是虚拟文件. 它于[虚拟文件系统](../Virtual-File-System.md)完全集成].
+ABP会考虑到这些依赖关系, 在view/page中使用正确的方法添加部件 . 样式和script可以是物理文件也可以是虚拟文件. 它于[虚拟文件系统](../../Virtual-File-System.md)完全集成].
### 定义 Bundle
@@ -227,7 +227,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
某些组件可能只对通过身份验证或授权的用户可用,这时可以使用 `Widget` attribute 的以下属性:
* `RequiresAuthentication` (`bool`): 设置为true,只有通过身份验证的用户(登录用户)可用.
-* `RequiredPolicies` (`List`): 授权用户的策略名称列表. 有关策略的详细信息请参阅[授权文档](../Authorization.md).
+* `RequiredPolicies` (`List`): 授权用户的策略名称列表. 有关策略的详细信息请参阅[授权文档](../../Authorization.md).
示例:
@@ -260,7 +260,7 @@ Configure(options =>
});
```
-将上面的代码写到[模块](../Module-Development-Basics.md)的 `ConfigureServices` 方法中. `AbpWidgetOptions` 可以完成 `Widget` attribute 的所有功能. 比如为组件添加样式:
+将上面的代码写到[模块](../../Module-Development-Basics.md)的 `ConfigureServices` 方法中. `AbpWidgetOptions` 可以完成 `Widget` attribute 的所有功能. 比如为组件添加样式:
````csharp
Configure(options =>
diff --git a/docs/zh-Hans/images/bookstore-added-brand-files.png b/docs/zh-Hans/images/bookstore-added-brand-files.png
new file mode 100644
index 0000000000..15ef283a2a
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-added-brand-files.png differ
diff --git a/docs/zh-Hans/images/bookstore-added-logo.png b/docs/zh-Hans/images/bookstore-added-logo.png
new file mode 100644
index 0000000000..55af77a5f4
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-added-logo.png differ
diff --git a/docs/zh-Hans/images/bookstore-added-role-js-file.png b/docs/zh-Hans/images/bookstore-added-role-js-file.png
new file mode 100644
index 0000000000..00881bf808
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-added-role-js-file.png differ
diff --git a/docs/zh-Hans/images/bookstore-brand-area-highlighted.png b/docs/zh-Hans/images/bookstore-brand-area-highlighted.png
new file mode 100644
index 0000000000..7ab1b8fb7f
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-brand-area-highlighted.png differ
diff --git a/docs/zh-Hans/images/bookstore-global-css-file.png b/docs/zh-Hans/images/bookstore-global-css-file.png
new file mode 100644
index 0000000000..e764fef8ee
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-global-css-file.png differ
diff --git a/docs/zh-Hans/images/bookstore-google-analytics-view-component.png b/docs/zh-Hans/images/bookstore-google-analytics-view-component.png
new file mode 100644
index 0000000000..e548a00935
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-google-analytics-view-component.png differ
diff --git a/docs/zh-Hans/images/bookstore-menus-highlighted.png b/docs/zh-Hans/images/bookstore-menus-highlighted.png
new file mode 100644
index 0000000000..c4f3237b00
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-menus-highlighted.png differ
diff --git a/docs/zh-Hans/images/bookstore-notification-icon-on-toolbar.png b/docs/zh-Hans/images/bookstore-notification-icon-on-toolbar.png
new file mode 100644
index 0000000000..817491e9eb
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-notification-icon-on-toolbar.png differ
diff --git a/docs/zh-Hans/images/bookstore-notification-view-component.png b/docs/zh-Hans/images/bookstore-notification-view-component.png
new file mode 100644
index 0000000000..3fc300efe1
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-notification-view-component.png differ
diff --git a/docs/zh-Hans/images/bookstore-toolbar-highlighted.png b/docs/zh-Hans/images/bookstore-toolbar-highlighted.png
new file mode 100644
index 0000000000..f2b7c3f57e
Binary files /dev/null and b/docs/zh-Hans/images/bookstore-toolbar-highlighted.png differ
diff --git a/docs/zh-Hans/images/docs-module_download-new-abp-project.png b/docs/zh-Hans/images/docs-module_download-new-abp-project.png
index 0da3b7a67a..6424ad7622 100644
Binary files a/docs/zh-Hans/images/docs-module_download-new-abp-project.png and b/docs/zh-Hans/images/docs-module_download-new-abp-project.png differ
diff --git a/docs/zh-Hans/images/docs-module_solution-explorer.png b/docs/zh-Hans/images/docs-module_solution-explorer.png
index cafc38f0b0..2988ec4134 100644
Binary files a/docs/zh-Hans/images/docs-module_solution-explorer.png and b/docs/zh-Hans/images/docs-module_solution-explorer.png differ
diff --git a/docs/zh-Hans/images/microservice-sample-diagram-3.png b/docs/zh-Hans/images/microservice-sample-diagram-3.png
new file mode 100644
index 0000000000..b8066ef6be
Binary files /dev/null and b/docs/zh-Hans/images/microservice-sample-diagram-3.png differ
diff --git a/docs/zh-Hans/images/microservice-sample-solution-2.png b/docs/zh-Hans/images/microservice-sample-solution-2.png
new file mode 100644
index 0000000000..3da5d160f8
Binary files /dev/null and b/docs/zh-Hans/images/microservice-sample-solution-2.png differ
diff --git a/docs/zh-Hans/images/overriding-login-cshtml.png b/docs/zh-Hans/images/overriding-login-cshtml.png
new file mode 100644
index 0000000000..d6daa4a141
Binary files /dev/null and b/docs/zh-Hans/images/overriding-login-cshtml.png differ
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs
index 01d3a35db2..60d1307b38 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs
@@ -8,7 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
{
public const string Name = "Basic";
- public string GetLayout(string name, bool fallbackToDefault = true)
+ public virtual string GetLayout(string name, bool fallbackToDefault = true)
{
switch (name)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs
index c808250a3e..31768b5e6a 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs
@@ -1,4 +1,5 @@
using System.IO;
+using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Http
{
@@ -11,5 +12,13 @@ namespace Microsoft.AspNetCore.Http
return stream.GetAllBytes();
}
}
+
+ public static async Task GetAllBytesAsync(this IFormFile file)
+ {
+ using (var stream = file.OpenReadStream())
+ {
+ return await stream.GetAllBytesAsync();
+ }
+ }
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
index 06ce49551b..21a675ddc4 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
@@ -43,21 +43,6 @@ namespace Volo.Abp.AspNetCore.Mvc
public override void ConfigureServices(ServiceConfigurationContext context)
{
- //Configure Razor
- context.Services.Insert(0,
- ServiceDescriptor.Singleton>(
- new ConfigureOptions(options =>
- {
- options.FileProviders.Add(
- new RazorViewEngineVirtualFileProvider(
- context.Services.GetSingletonInstance>()
- )
- );
- }
- )
- )
- );
-
Configure(options =>
{
options.IgnoredInterfaces.AddIfNotContains(typeof(IAsyncActionFilter));
@@ -101,7 +86,16 @@ namespace Volo.Abp.AspNetCore.Mvc
};
})
.AddViewLocalization(); //TODO: How to configure from the application? Also, consider to move to a UI module since APIs does not care about it.
-
+
+ Configure(options =>
+ {
+ options.FileProviders.Add(
+ new RazorViewEngineVirtualFileProvider(
+ context.Services.GetSingletonInstance>()
+ )
+ );
+ });
+
context.Services.ExecutePreConfiguredActions(mvcBuilder);
//TODO: AddViewLocalization by default..?
diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs
index 1ce71ca3f9..3326c4e65f 100644
--- a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs
+++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs
@@ -1,5 +1,7 @@
+using System;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -11,14 +13,34 @@ namespace Volo.Abp.BackgroundWorkers
public abstract class BackgroundWorkerBase : IBackgroundWorker
{
//TODO: Add UOW, Localization and other useful properties..?
+ public IServiceProvider ServiceProvider { get; set; }
+ protected readonly object ServiceProviderLock = new object();
- public ILogger Logger { protected get; set; }
+ protected TService LazyGetRequiredService(ref TService reference)
+ => LazyGetRequiredService(typeof(TService), ref reference);
- protected BackgroundWorkerBase()
+ protected TRef LazyGetRequiredService(Type serviceType, ref TRef reference)
{
- Logger = NullLogger.Instance;
+ if (reference == null)
+ {
+ lock (ServiceProviderLock)
+ {
+ if (reference == null)
+ {
+ reference = (TRef)ServiceProvider.GetRequiredService(serviceType);
+ }
+ }
+ }
+
+ return reference;
}
-
+
+ public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
+ private ILoggerFactory _loggerFactory;
+
+ protected ILogger Logger => _lazyLogger.Value;
+ private Lazy _lazyLogger => new Lazy(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);
+
public virtual Task StartAsync(CancellationToken cancellationToken = default)
{
Logger.LogDebug("Started background worker: " + ToString());
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs
index a33c5f56ec..2b38059ef8 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs
@@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
-using Volo.Abp.MultiTenancy;
-using Volo.Abp.Reflection;
namespace Volo.Abp.Domain.Entities
{
@@ -13,12 +11,24 @@ namespace Volo.Abp.Domain.Entities
///
public static class EntityHelper
{
-
public static bool IsEntity([NotNull] Type type)
{
return typeof(IEntity).IsAssignableFrom(type);
}
+ public static bool IsEntityWithId([NotNull] Type type)
+ {
+ foreach (var interfaceType in type.GetInterfaces())
+ {
+ if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public static bool HasDefaultId(IEntity entity)
{
if (EqualityComparer.Default.Equals(entity.Id, default))
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
index 2ac16ec229..3bda51bd2e 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
@@ -18,6 +18,34 @@ namespace Volo.Abp.Domain.Repositories
public interface IRepository : IReadOnlyRepository, IBasicRepository
where TEntity : class, IEntity
{
+ ///
+ /// Get a single entity by the given .
+ /// It returns null if no entity with the given .
+ /// It throws if there are multiple entities with the given .
+ ///
+ /// A condition to find the entity
+ /// Set true to include all children of this entity
+ /// A to observe while waiting for the task to complete.
+ Task FindAsync(
+ [NotNull] Expression> predicate,
+ bool includeDetails = true,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Get a single entity by the given .
+ /// It throws if there is no entity with the given .
+ /// It throws if there are multiple entities with the given .
+ ///
+ /// A condition to filter entities
+ /// Set true to include all children of this entity
+ /// A to observe while waiting for the task to complete.
+ Task GetAsync(
+ [NotNull] Expression> predicate,
+ bool includeDetails = true,
+ CancellationToken cancellationToken = default
+ );
+
///
/// Deletes many entities by function.
/// Notice that: All entities fits to given predicate are retrieved and deleted.
@@ -30,7 +58,11 @@ namespace Volo.Abp.Domain.Repositories
/// This is useful for ORMs / database APIs those only save changes with an explicit method call, but you need to immediately save changes to the database.
///
/// A to observe while waiting for the task to complete.
- Task DeleteAsync([NotNull] Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default);
+ Task DeleteAsync(
+ [NotNull] Expression> predicate,
+ bool autoSave = false,
+ CancellationToken cancellationToken = default
+ );
}
public interface IRepository : IRepository, IReadOnlyRepository, IBasicRepository
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
index 3ade403017..bd7e486d29 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
@@ -49,6 +49,26 @@ namespace Volo.Abp.Domain.Repositories
protected abstract IQueryable GetQueryable();
+ public abstract Task FindAsync(
+ Expression> predicate,
+ bool includeDetails = true,
+ CancellationToken cancellationToken = default);
+
+ public async Task GetAsync(
+ Expression> predicate,
+ bool includeDetails = true,
+ CancellationToken cancellationToken = default)
+ {
+ var entity = await FindAsync(predicate, includeDetails, cancellationToken);
+
+ if (entity == null)
+ {
+ throw new EntityNotFoundException(typeof(TEntity));
+ }
+
+ return entity;
+ }
+
public abstract Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default);
protected virtual TQueryable ApplyDataFilters(TQueryable query)
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
index 106e00f77a..a48e5d7da5 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
@@ -93,6 +93,20 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
return DbSet.AsQueryable();
}
+ public override async Task FindAsync(
+ Expression> predicate,
+ bool includeDetails = true,
+ CancellationToken cancellationToken = default)
+ {
+ return includeDetails
+ ? await WithDetails()
+ .Where(predicate)
+ .SingleOrDefaultAsync(GetCancellationToken(cancellationToken))
+ : await DbSet
+ .Where(predicate)
+ .SingleOrDefaultAsync(GetCancellationToken(cancellationToken));
+ }
+
public override async Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
var entities = await GetQueryable()
@@ -173,18 +187,6 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
}
- public virtual TEntity Get(TKey id, bool includeDetails = true)
- {
- var entity = Find(id, includeDetails);
-
- if (entity == null)
- {
- throw new EntityNotFoundException(typeof(TEntity), id);
- }
-
- return entity;
- }
-
public virtual async Task GetAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
var entity = await FindAsync(id, includeDetails, GetCancellationToken(cancellationToken));
@@ -197,13 +199,6 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
return entity;
}
- public virtual TEntity Find(TKey id, bool includeDetails = true)
- {
- return includeDetails
- ? WithDetails().FirstOrDefault(e => e.Id.Equals(id))
- : DbSet.Find(id);
- }
-
public virtual async Task FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
return includeDetails
diff --git a/framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj b/framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj
index 8aa654922a..d282ce5269 100644
--- a/framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj
+++ b/framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
index b81aa7ae87..2457c3c453 100644
--- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
+++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
@@ -31,6 +31,14 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
return ApplyDataFilters(Collection.AsQueryable());
}
+ public override Task FindAsync(
+ Expression> predicate,
+ bool includeDetails = true,
+ CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(Collection.AsQueryable().Where(predicate).SingleOrDefault());
+ }
+
public override Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
var entities = Collection.AsQueryable().Where(predicate).ToList();
diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
index 3333f1d505..5533cb9f74 100644
--- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
+++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
@@ -169,6 +169,16 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
return GetMongoQueryable();
}
+ public override async Task FindAsync(
+ Expression> predicate,
+ bool includeDetails = true,
+ CancellationToken cancellationToken = default)
+ {
+ return await GetMongoQueryable()
+ .Where(predicate)
+ .SingleOrDefaultAsync(GetCancellationToken(cancellationToken));
+ }
+
public virtual IMongoQueryable GetMongoQueryable()
{
return ApplyDataFilters(
diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs
index 7e1dd24ae6..067f4b6a01 100644
--- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs
+++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs
@@ -246,6 +246,11 @@ namespace Volo.Abp.Domain.Repositories
throw new NotImplementedException();
}
+ public override Task FindAsync(Expression> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
+ {
+ throw new NotImplementedException();
+ }
+
public override Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestDataBuilder.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestDataBuilder.cs
index 6d1be41f31..03e4ed5465 100644
--- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestDataBuilder.cs
+++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestDataBuilder.cs
@@ -41,7 +41,7 @@ namespace Volo.Abp.TestApp
{
var istanbul = new City(IstanbulCityId, "Istanbul");
istanbul.Districts.Add(new District(istanbul.Id, "Bakirkoy", 1283999));
- istanbul.Districts.Add(new District(istanbul.Id, "Mecidiyek�y", 2222321));
+ istanbul.Districts.Add(new District(istanbul.Id, "Mecidiyekoy", 2222321));
istanbul.Districts.Add(new District(istanbul.Id, "Uskudar", 726172));
await _cityRepository.InsertAsync(new City(Guid.NewGuid(), "Tokyo"));
diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs
index 07e6211c09..28c94b02fc 100644
--- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs
+++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs
@@ -28,6 +28,14 @@ namespace Volo.Abp.TestApp.Testing
person.Phones.Count.ShouldBe(2);
}
+ [Fact]
+ public async Task GetAsync_With_Predicate()
+ {
+ var person = await PersonRepository.GetAsync(p => p.Name == "Douglas");
+ person.Name.ShouldBe("Douglas");
+ person.Phones.Count.ShouldBe(2);
+ }
+
[Fact]
public async Task FindAsync_Should_Return_Null_For_Not_Found_Entity()
{
@@ -35,6 +43,14 @@ namespace Volo.Abp.TestApp.Testing
person.ShouldBeNull();
}
+ [Fact]
+ public async Task FindAsync_Should_Return_Null_For_Not_Found_Entity_With_Predicate()
+ {
+ var randomName = Guid.NewGuid().ToString();
+ var person = await PersonRepository.FindAsync(p => p.Name == randomName);
+ person.ShouldBeNull();
+ }
+
[Fact]
public async Task DeleteAsync()
{
diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
index 2ab190b724..a38a51a16c 100644
--- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
+++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
@@ -32,4 +32,8 @@
+
+
+
+
diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityRoleAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityRoleAppService.cs
index 0f573634a4..9381a9b2c8 100644
--- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityRoleAppService.cs
+++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityRoleAppService.cs
@@ -8,6 +8,8 @@ namespace Volo.Abp.Identity
{
public interface IIdentityRoleAppService : IApplicationService
{
+ Task> GetAllListAsync();
+
Task> GetListAsync(PagedAndSortedResultRequestDto input);
Task CreateAsync(IdentityRoleCreateDto input);
diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityRoleAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityRoleAppService.cs
index ffc13fdbca..b02319bc24 100644
--- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityRoleAppService.cs
+++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityRoleAppService.cs
@@ -27,6 +27,13 @@ namespace Volo.Abp.Identity
await _roleManager.GetByIdAsync(id));
}
+ public virtual async Task> GetAllListAsync()
+ {
+ var list = await _roleRepository.GetListAsync();
+ return new ListResultDto(
+ ObjectMapper.Map, List>(list));
+ }
+
public virtual async Task> GetListAsync(PagedAndSortedResultRequestDto input)
{
var list = await _roleRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount);
diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json
index 025bb7fd87..d6d6ca3983 100644
--- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json
+++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json
@@ -82,6 +82,7 @@
"DisplayName:Abp.Identity.Lockout.LockoutDuration": "Lockout duration(seconds)",
"DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "Max failed access attempts",
"DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "Require confirmed email",
+ "DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Enable phone number confirmation",
"DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Require confirmed phoneNumber",
"DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "Is username update enabled",
"DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "Is email update enabled",
@@ -95,6 +96,7 @@
"Description:Abp.Identity.Lockout.LockoutDuration": "The duration a user is locked out for when a lockout occurs.",
"Description:Abp.Identity.Lockout.MaxFailedAccessAttempts": "The number of failed access attempts allowed before a user is locked out, assuming lock out is enabled.",
"Description:Abp.Identity.SignIn.RequireConfirmedEmail": "Whether a confirmed email address is required to sign in.",
+ "Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Whether the phoneNumber can be confirmed by the user.",
"Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Whether a confirmed telephone number is required to sign in.",
"Description:Abp.Identity.User.IsUserNameUpdateEnabled": "Whether the username can be updated by the user.",
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "Whether the email can be updated by the user."
diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs
index babeb4fb77..9c07f6c55b 100644
--- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs
+++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs
@@ -30,6 +30,7 @@
private const string SignInPrefix = Prefix + ".SignIn";
public const string RequireConfirmedEmail = SignInPrefix + ".RequireConfirmedEmail";
+ public const string EnablePhoneNumberConfirmation = SignInPrefix + ".EnablePhoneNumberConfirmation";
public const string RequireConfirmedPhoneNumber = SignInPrefix + ".RequireConfirmedPhoneNumber";
}
diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs
index efae6e467a..92847004ea 100644
--- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs
+++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs
@@ -72,6 +72,11 @@ namespace Volo.Abp.Identity
false.ToString(), L("DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail"),
L("Description:Abp.Identity.SignIn.RequireConfirmedEmail"),
true),
+ new SettingDefinition(
+ IdentitySettingNames.SignIn.EnablePhoneNumberConfirmation,
+ true.ToString(), L("DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"),
+ L("Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"),
+ true),
new SettingDefinition(
IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber,
false.ToString(), L("DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"),
diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs
index 09760109d0..9925d18abb 100644
--- a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs
+++ b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Application.Dtos;
@@ -19,6 +20,13 @@ namespace Volo.Abp.Identity
_roleAppService = roleAppService;
}
+ [HttpGet]
+ [Route("all")]
+ public virtual Task> GetAllListAsync()
+ {
+ return _roleAppService.GetAllListAsync();
+ }
+
[HttpGet]
public virtual Task> GetListAsync(PagedAndSortedResultRequestDto input)
{
diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs
index aad90e73cc..6c1ac1ff72 100644
--- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs
+++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs
@@ -29,9 +29,9 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users
{
UserInfo = new UserInfoViewModel();
- var roleDtoList = await _identityRoleAppService.GetListAsync(new PagedAndSortedResultRequestDto());
+ var roleDtoList = (await _identityRoleAppService.GetAllListAsync()).Items;
- Roles = ObjectMapper.Map, AssignedRoleViewModel[]>(roleDtoList.Items);
+ Roles = ObjectMapper.Map, AssignedRoleViewModel[]>(roleDtoList);
foreach (var role in Roles)
{
diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs
index 6c8d40c14d..98a32fd1bd 100644
--- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs
+++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs
@@ -31,9 +31,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users
{
UserInfo = ObjectMapper.Map(await _identityUserAppService.GetAsync(id));
- Roles = ObjectMapper.Map, AssignedRoleViewModel[]>(
- (await _identityRoleAppService.GetListAsync(new PagedAndSortedResultRequestDto())).Items
- );
+ Roles = ObjectMapper.Map, AssignedRoleViewModel[]>((await _identityRoleAppService.GetAllListAsync()).Items);
var userRoleNames = (await _identityUserAppService.GetRolesAsync(UserInfo.Id)).Items.Select(r => r.Name).ToList();
foreach (var role in Roles)
diff --git a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityRoleAppService_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityRoleAppService_Tests.cs
index 663cc07786..5d64bb6c78 100644
--- a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityRoleAppService_Tests.cs
+++ b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityRoleAppService_Tests.cs
@@ -34,6 +34,18 @@ namespace Volo.Abp.Identity
result.Id.ShouldBe(moderator.Id);
}
+ [Fact]
+ public async Task GetAllListAsync()
+ {
+ //Act
+
+ var result = await _roleAppService.GetAllListAsync();
+
+ //Assert
+
+ result.Items.Count.ShouldBeGreaterThan(0);
+ }
+
[Fact]
public async Task GetListAsync()
{
diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs
index 7561835ee3..189d7d0f80 100644
--- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs
+++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs
@@ -1,7 +1,22 @@
-namespace Volo.Abp.TenantManagement
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Text.RegularExpressions;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Localization;
+using Volo.Abp.TenantManagement.Localization;
+
+namespace Volo.Abp.TenantManagement
{
public class TenantCreateDto : TenantCreateOrUpdateDtoBase
{
+ [Required]
+ [EmailAddress]
+ [MaxLength(256)]
+ public string AdminEmailAddress { get; set; }
+
+ [Required]
+ [MaxLength(128)]
+ public string AdminPassword { get; set; }
}
}
\ No newline at end of file
diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs
index a5a9b4af3d..b02da79b79 100644
--- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs
+++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs
@@ -1,7 +1,11 @@
-namespace Volo.Abp.TenantManagement
+using System.ComponentModel.DataAnnotations;
+
+namespace Volo.Abp.TenantManagement
{
public abstract class TenantCreateOrUpdateDtoBase
{
+ [Required]
+ [StringLength(TenantConsts.MaxNameLength)]
public string Name { get; set; }
}
}
\ No newline at end of file
diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs
index 3dabc569bd..cbe9b67c76 100644
--- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs
+++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs
@@ -15,7 +15,7 @@ namespace Volo.Abp.TenantManagement
protected ITenantManager TenantManager { get; }
public TenantAppService(
- ITenantRepository tenantRepository,
+ ITenantRepository tenantRepository,
ITenantManager tenantManager,
IDataSeeder dataSeeder)
{
@@ -51,10 +51,13 @@ namespace Volo.Abp.TenantManagement
{
//TODO: Handle database creation?
- //TODO: Set admin email & password..?
- await DataSeeder.SeedAsync(tenant.Id);
+ await DataSeeder.SeedAsync(
+ new DataSeedContext(tenant.Id)
+ .WithProperty("AdminEmail", input.AdminEmailAddress)
+ .WithProperty("AdminPassword", input.AdminPassword)
+ );
}
-
+
return ObjectMapper.Map(tenant);
}
diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json
index fc51f00750..d6d1d7c155 100644
--- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json
+++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json
@@ -15,6 +15,8 @@
"Permission:Edit": "Edit",
"Permission:Delete": "Delete",
"Permission:ManageConnectionStrings": "Manage connection strings",
- "Permission:ManageFeatures": "Manage features"
+ "Permission:ManageFeatures": "Manage features",
+ "DisplayName:AdminEmailAddress": "Admin Email Address",
+ "DisplayName:AdminPassword": "Admin Password"
}
}
\ No newline at end of file
diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json
index 1b6bdca2c2..72b570f3f9 100644
--- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json
+++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json
@@ -15,6 +15,8 @@
"Permission:Edit": "Düzenleme",
"Permission:Delete": "Silme",
"Permission:ManageConnectionStrings": "Bağlantı cümlelerini yönet",
- "Permission:ManageFeatures": "Özellikleri yönet"
+ "Permission:ManageFeatures": "Özellikleri yönet",
+ "DisplayName:AdminEmailAddress": "Admin Eposta Adresi",
+ "DisplayName:AdminPassword": "Admin Şifresi"
}
}
\ No newline at end of file
diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs
index 2aabf871de..df8e513003 100644
--- a/modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs
+++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs
@@ -35,6 +35,7 @@ namespace Volo.Abp.TenantManagement
[HttpPost]
public virtual Task CreateAsync(TenantCreateDto input)
{
+ ValidateModel();
return _service.CreateAsync(input);
}
diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml
index b3a2c9d5be..8593679a7c 100644
--- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml
+++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml
@@ -12,7 +12,11 @@
-
+
+
+
+
+
diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs
index a602b12a65..23c71bba96 100644
--- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs
+++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs
@@ -31,8 +31,16 @@ namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants
{
[Required]
[StringLength(TenantConsts.MaxNameLength)]
- [Display(Name = "DisplayName:TenantName")]
public string Name { get; set; }
+
+ [Required]
+ [EmailAddress]
+ [MaxLength(256)]
+ public string AdminEmailAddress { get; set; }
+
+ [Required]
+ [MaxLength(128)]
+ public string AdminPassword { get; set; }
}
}
}
diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs
index 3e44e7c8ea..b27ceff6dc 100644
--- a/modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs
+++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs
@@ -59,7 +59,7 @@ namespace Volo.Abp.TenantManagement
public async Task CreateAsync()
{
var tenancyName = Guid.NewGuid().ToString("N").ToLowerInvariant();
- var tenant = await _tenantAppService.CreateAsync(new TenantCreateDto { Name = tenancyName });
+ var tenant = await _tenantAppService.CreateAsync(new TenantCreateDto { Name = tenancyName , AdminEmailAddress = "admin@admin.com", AdminPassword = "123456"});
tenant.Name.ShouldBe(tenancyName);
tenant.Id.ShouldNotBe(default(Guid));
}
@@ -69,7 +69,7 @@ namespace Volo.Abp.TenantManagement
{
await Assert.ThrowsAsync(async () =>
{
- await _tenantAppService.CreateAsync(new TenantCreateDto { Name = "acme" });
+ await _tenantAppService.CreateAsync(new TenantCreateDto { Name = "acme", AdminEmailAddress = "admin@admin.com", AdminPassword = "123456" });
});
}
diff --git a/npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts b/npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts
index 6c4c9b016c..1f268b4572 100644
--- a/npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts
+++ b/npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts
@@ -3,7 +3,6 @@ import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgModule } from '@angular/core';
import { ThemeBasicModule } from '@abp/ng.theme.basic';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
-import { TableModule } from 'primeng/table';
import { NgxValidateCoreModule } from '@ngx-validate/core';
@NgModule({
@@ -12,7 +11,6 @@ import { NgxValidateCoreModule } from '@ngx-validate/core';
CoreModule,
ThemeSharedModule,
ThemeBasicModule,
- TableModule,
NgbDropdownModule,
NgxValidateCoreModule,
],
@@ -20,7 +18,6 @@ import { NgxValidateCoreModule } from '@ngx-validate/core';
CoreModule,
ThemeSharedModule,
ThemeBasicModule,
- TableModule,
NgbDropdownModule,
NgxValidateCoreModule,
],
diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json
index e020b3f235..93ef5c8ae7 100644
--- a/npm/ng-packs/package.json
+++ b/npm/ng-packs/package.json
@@ -21,19 +21,19 @@
"generate:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
},
"devDependencies": {
- "@abp/ng.account": "~2.1.0",
- "@abp/ng.account.config": "~2.1.0",
- "@abp/ng.core": "~2.1.0",
- "@abp/ng.feature-management": "~2.1.0",
- "@abp/ng.identity": "~2.1.0",
- "@abp/ng.identity.config": "~2.1.0",
- "@abp/ng.permission-management": "~2.1.0",
- "@abp/ng.setting-management": "~2.1.0",
- "@abp/ng.setting-management.config": "~2.1.0",
- "@abp/ng.tenant-management": "~2.1.0",
- "@abp/ng.tenant-management.config": "~2.1.0",
- "@abp/ng.theme.basic": "~2.1.0",
- "@abp/ng.theme.shared": "~2.1.0",
+ "@abp/ng.account": "~2.2.0",
+ "@abp/ng.account.config": "~2.2.0",
+ "@abp/ng.core": "~2.2.0",
+ "@abp/ng.feature-management": "~2.2.0",
+ "@abp/ng.identity": "~2.2.0",
+ "@abp/ng.identity.config": "~2.2.0",
+ "@abp/ng.permission-management": "~2.2.0",
+ "@abp/ng.setting-management": "~2.2.0",
+ "@abp/ng.setting-management.config": "~2.2.0",
+ "@abp/ng.tenant-management": "~2.2.0",
+ "@abp/ng.tenant-management.config": "~2.2.0",
+ "@abp/ng.theme.basic": "~2.2.0",
+ "@abp/ng.theme.shared": "~2.2.0",
"@angular-builders/jest": "^8.2.0",
"@angular-devkit/build-angular": "~0.803.21",
"@angular-devkit/build-ng-packagr": "~0.803.21",
diff --git a/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts b/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts
index d8fe9762e1..fd66913ca5 100644
--- a/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts
+++ b/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts
@@ -1,9 +1,9 @@
-import { ABP, SetTenant, SessionState } from '@abp/ng.core';
+import { ABP, SetTenant, SessionState, GetAppConfiguration } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngxs/store';
import { throwError } from 'rxjs';
-import { catchError, take, finalize } from 'rxjs/operators';
+import { catchError, take, finalize, switchMap } from 'rxjs/operators';
import snq from 'snq';
import { AccountService } from '../../services/account.service';
import { Account } from '../../models/account';
@@ -52,29 +52,32 @@ export class TenantBoxComponent
);
return throwError(err);
}),
+ switchMap(({ success, tenantId }) => {
+ if (success) {
+ this.tenant = {
+ id: tenantId,
+ name: this.tenant.name,
+ };
+ this.tenantName = this.tenant.name;
+ this.isModalVisible = false;
+ } else {
+ this.toasterService.error(
+ 'AbpUiMultiTenancy::GivenTenantIsNotAvailable',
+ 'AbpUi::Error',
+ {
+ messageLocalizationParams: [this.tenant.name],
+ },
+ );
+ this.tenant = {} as ABP.BasicItem;
+ this.tenantName = '';
+ }
+ this.store.dispatch(new SetTenant(success ? this.tenant : null));
+ return this.store.dispatch(new GetAppConfiguration());
+ }),
)
- .subscribe(({ success, tenantId }) => {
- if (success) {
- this.tenant = {
- id: tenantId,
- name: this.tenant.name,
- };
- this.tenantName = this.tenant.name;
- this.isModalVisible = false;
- } else {
- this.toasterService.error(
- 'AbpUiMultiTenancy::GivenTenantIsNotAvailable',
- 'AbpUi::Error',
- {
- messageLocalizationParams: [this.tenant.name],
- },
- );
- this.tenant = {} as ABP.BasicItem;
- }
- this.store.dispatch(new SetTenant(success ? this.tenant : null));
- });
+ .subscribe();
} else {
- this.store.dispatch(new SetTenant(null));
+ this.store.dispatch([new SetTenant(null), new GetAppConfiguration()]);
this.tenantName = null;
this.isModalVisible = false;
}
diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts
index f424d83cb2..5269946391 100644
--- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts
+++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts
@@ -1,11 +1,12 @@
+import { Injectable } from '@angular/core';
import { Action, createSelector, Selector, State, StateContext, Store } from '@ngxs/store';
import { of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import snq from 'snq';
import {
+ AddRoute,
GetAppConfiguration,
PatchRouteByName,
- AddRoute,
SetEnvironment,
} from '../actions/config.actions';
import { SetLanguage } from '../actions/session.actions';
@@ -14,7 +15,6 @@ import { Config } from '../models/config';
import { ApplicationConfigurationService } from '../services/application-configuration.service';
import { organizeRoutes } from '../utils/route-utils';
import { SessionState } from './session.state';
-import { Injectable } from '@angular/core';
@State({
name: 'ConfigState',
@@ -91,24 +91,24 @@ export class ConfigState {
const selector = createSelector([ConfigState], (state: Config.State) => {
return snq(() => state.setting.values[key]);
});
+
return selector;
}
static getSettings(keyword?: string) {
const selector = createSelector([ConfigState], (state: Config.State) => {
- if (keyword) {
- const keys = snq(
- () => Object.keys(state.setting.values).filter(key => key.indexOf(keyword) > -1),
- [],
- );
-
- if (keys.length) {
- return keys.reduce((acc, key) => ({ ...acc, [key]: state.setting.values[key] }), {});
- }
- }
+ const settings = snq(() => state.setting.values, {});
+
+ if (!keyword) return settings;
- return snq(() => state.setting.values, {});
+ const keysFound = Object.keys(settings).filter(key => key.indexOf(keyword) > -1);
+
+ return keysFound.reduce((acc, key) => {
+ acc[key] = settings[key];
+ return acc;
+ }, {});
});
+
return selector;
}
diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts
index 635ca1d8b1..07baed4c14 100644
--- a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts
+++ b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts
@@ -1,12 +1,12 @@
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest';
import { Store } from '@ngxs/store';
-import { ReplaySubject, timer, Subject, of } from 'rxjs';
+import clone from 'just-clone';
+import { of, ReplaySubject, timer } from 'rxjs';
+import { AddRoute, PatchRouteByName, SetLanguage } from '../actions';
+import { ABP } from '../models';
import { Config } from '../models/config';
import { ApplicationConfigurationService, ConfigStateService } from '../services';
import { ConfigState } from '../states';
-import { SetLanguage, PatchRouteByName, AddRoute } from '../actions';
-import clone from 'just-clone';
-import { ABP } from '../models';
export const CONFIG_STATE_DATA = {
environment: {
@@ -116,6 +116,7 @@ export const CONFIG_STATE_DATA = {
},
setting: {
values: {
+ 'Abp.Custom.SomeSetting': 'X',
'Abp.Localization.DefaultLanguage': 'en',
},
},
@@ -218,14 +219,14 @@ describe('ConfigState', () => {
});
describe('#getSettings', () => {
- it('should return settings', () => {
- expect(ConfigState.getSettings('Localization')(CONFIG_STATE_DATA)).toEqual({
- 'Abp.Localization.DefaultLanguage': 'en',
- });
-
- expect(ConfigState.getSettings('AllSettings')(CONFIG_STATE_DATA)).toEqual(
- CONFIG_STATE_DATA.setting.values,
- );
+ test.each`
+ keyword | expected
+ ${undefined} | ${CONFIG_STATE_DATA.setting.values}
+ ${'Localization'} | ${{ 'Abp.Localization.DefaultLanguage': 'en' }}
+ ${'X'} | ${{}}
+ ${'localization'} | ${{}}
+ `('should return $expected when keyword is given as $keyword', ({ keyword, expected }) => {
+ expect(ConfigState.getSettings(keyword)(CONFIG_STATE_DATA)).toEqual(expected);
});
});
diff --git a/npm/ng-packs/scripts/install-new-dependencies.ts b/npm/ng-packs/scripts/install-new-dependencies.ts
index 52f93f23d4..156851f657 100644
--- a/npm/ng-packs/scripts/install-new-dependencies.ts
+++ b/npm/ng-packs/scripts/install-new-dependencies.ts
@@ -17,7 +17,7 @@ const updateAndInstall = async () => {
...packageJson.devDependencies,
...dependencies,
...peerDependencies,
- ...{ [name]: `^${version}` },
+ ...{ [name]: `~${version}` },
};
packageJson.devDependencies = Object.keys(packageJson.devDependencies)
diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj
index 897e84c66e..c95c5a148f 100644
--- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj
+++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj
@@ -16,6 +16,7 @@
+
diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json
new file mode 100644
index 0000000000..687d42579c
--- /dev/null
+++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json
@@ -0,0 +1,6 @@
+{
+ "culture": "sl",
+ "texts": {
+ "ManageYourProfile": "Upravljajte svojim profilom"
+ }
+}
\ No newline at end of file
diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json
new file mode 100644
index 0000000000..99586f01c9
--- /dev/null
+++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json
@@ -0,0 +1,6 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "ManageYourProfile": "管理个人资料"
+ }
+}
\ No newline at end of file
diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json
new file mode 100644
index 0000000000..ceea055597
--- /dev/null
+++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json
@@ -0,0 +1,6 @@
+{
+ "culture": "zh-Hant",
+ "texts": {
+ "ManageYourProfile": "管理個人資料"
+ }
+}
\ No newline at end of file