diff --git a/.github/workflows/auto-pr.yml b/.github/workflows/auto-pr.yml
index 476182f785..987a3ada1b 100644
--- a/.github/workflows/auto-pr.yml
+++ b/.github/workflows/auto-pr.yml
@@ -17,7 +17,7 @@ jobs:
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
- branch: auto-rel-merge-pr-${{github.run_number}}
+ branch: auto-merge/rel-4-2/${{github.run_number}}
title: Merge branch dev with rel-4.2
body: This PR generated automatically to merge dev with rel-4.2. Please review the changed files before merging to prevent any errors that may occur.
reviewers: ${{github.actor}}
diff --git a/common.props b/common.props
index bb868967e9..39ce965ec0 100644
--- a/common.props
+++ b/common.props
@@ -21,4 +21,4 @@
runtime; build; native; contentfiles; analyzers
-
\ No newline at end of file
+
diff --git a/docs/en/CLI.md b/docs/en/CLI.md
index 2e7b117799..daf86025c4 100644
--- a/docs/en/CLI.md
+++ b/docs/en/CLI.md
@@ -106,6 +106,12 @@ abp new Acme.BookStore
* `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D:\local-template` or `https://.../my-template-file.zip`).
* `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder.
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](Entity-Framework-Core-Other-DBMS.md) (after creating the solution).
+* `--database-management-system` or `-dbms`: Sets the database management system. Default is **SQL Server**. Supported DBMS's:
+ * `SqlServer`
+ * `MySQL`
+ * `SQLite`
+ * `Oracle-Devart`
+ * `PostgreSQL`
* `--local-framework-ref --abp-path`: Uses local projects references to the ABP framework instead of using the NuGet packages. This can be useful if you download the ABP Framework source code and have a local reference to the framework from your application.
* `--no-random-port`: Uses template's default ports.
@@ -419,4 +425,4 @@ abp bundle [options]
* ```--working-directory``` or ```-wd```: Specifies the working directory. This option is useful when executing directory doesn't contain a Blazor project file.
* ```--force``` or ```-f```: Forces to build project before generating references.
-`bundle` command reads the `appsettings.json` file inside the Blazor project for bundling options. For more details about managing style and script references in Blazor apps, see [Managing Global Scripts & Styles](UI/Blazor/Global-Scripts-Styles.md)
\ No newline at end of file
+`bundle` command reads the `appsettings.json` file inside the Blazor project for bundling options. For more details about managing style and script references in Blazor apps, see [Managing Global Scripts & Styles](UI/Blazor/Global-Scripts-Styles.md)
diff --git a/docs/en/Community-Articles/2020-04-19-Customize-the-SignIn-Manager/POST.md b/docs/en/Community-Articles/2020-04-19-Customize-the-SignIn-Manager/POST.md
index 1342407880..9ae426624f 100644
--- a/docs/en/Community-Articles/2020-04-19-Customize-the-SignIn-Manager/POST.md
+++ b/docs/en/Community-Articles/2020-04-19-Customize-the-SignIn-Manager/POST.md
@@ -71,7 +71,8 @@ public async override Task GetE
?? provider;
return new Microsoft.AspNetCore.Identity.ExternalLoginInfo(auth.Principal, provider, providerKey, providerDisplayName)
{
- AuthenticationTokens = auth.Properties.GetTokens()
+ AuthenticationTokens = auth.Properties.GetTokens(),
+ AuthenticationProperties = auth.Properties
};
}
````
diff --git a/docs/en/Module-Entity-Extensions.md b/docs/en/Module-Entity-Extensions.md
index 042c10b815..dc23d1e663 100644
--- a/docs/en/Module-Entity-Extensions.md
+++ b/docs/en/Module-Entity-Extensions.md
@@ -1,5 +1,7 @@
# Module Entity Extensions
+> This feature is not supported by the Blazor UI yet.
+
## Introduction
Module entity extension system is a **high level** extension system that allows you to **define new properties** for existing entities of the depended modules. It automatically **adds properties to the entity, database, HTTP API and the user interface** in a single point.
@@ -325,6 +327,108 @@ One of the following names can be used as the localization key:
Localization system searches for the key with the given order. Localized text are used on the table and the create/edit forms.
+### Navigation Properties / Foreign Keys
+
+It is supported to add an extension property to an entity that is Id of another entity (foreign key).
+
+#### Example: Associate a department to a user
+
+````csharp
+ObjectExtensionManager.Instance.Modules()
+ .ConfigureIdentity(identity =>
+ {
+ identity.ConfigureUser(user =>
+ {
+ user.AddOrUpdateProperty(
+ "DepartmentId",
+ property =>
+ {
+ property.UI.Lookup.Url = "/api/departments";
+ property.UI.Lookup.DisplayPropertyName = "name";
+ }
+ );
+ });
+ });
+````
+
+`UI.Lookup.Url` option takes a URL to get list of departments to select on edit/create forms. This endpoint can be a typical controller, an [auto API controller](API/Auto-API-Controllers.md) or any type of endpoint that returns a proper JSON response.
+
+An example implementation that returns a fixed list of departments (in real life, you get the list from a data source):
+
+````csharp
+[Route("api/departments")]
+public class DepartmentController : AbpController
+{
+ [HttpGet]
+ public async Task> GetAsync()
+ {
+ return new ListResultDto(
+ new[]
+ {
+ new DepartmentDto
+ {
+ Id = Guid.Parse("6267f0df-870f-4173-be44-d74b4b56d2bd"),
+ Name = "Human Resources"
+ },
+ new DepartmentDto
+ {
+ Id = Guid.Parse("21c7b61f-330c-489e-8b8c-80e0a78a5cc5"),
+ Name = "Production"
+ }
+ }
+ );
+ }
+}
+````
+
+This API returns such a JSON response:
+
+````json
+{
+ "items": [{
+ "id": "6267f0df-870f-4173-be44-d74b4b56d2bd",
+ "name": "Human Resources"
+ }, {
+ "id": "21c7b61f-330c-489e-8b8c-80e0a78a5cc5",
+ "name": "Production"
+ }]
+}
+````
+
+ABP can now show an auto-complete select component to pick the department while creating or editing a user:
+
+
+
+And shows the department name on the data table:
+
+
+
+#### Lookup Options
+
+`UI.Lookup` has the following options to customize how to read the response returned from the `Url`:
+
+* `Url`: The endpoint to get the list of target entities. This is used on edit and create forms.
+* `DisplayPropertyName`: The property in the JSON response to read the display name of the target entity to show on the UI. Default: `text`.
+* `ValuePropertyName`: The property in the JSON response to read the Id of the target entity. Default: `id`.
+* `FilterParamName`: ABP allows to search/filter the entity list on edit/create forms. This is especially useful if the target list contains a lot of items. In this case, you can return a limited list (top 100, for example) and allow user to search on the list. ABP sends filter text to the server (as a simple query string) with the name of this option. Default: `filter`.
+* `ResultListPropertyName`: By default, returned JSON result should contain the entity list in an `items` array. You can change the name of this field. Default: `items`.
+
+#### Lookup Properties: How Display Name Works?
+
+You may wonder how ABP shows the department name on the data table above.
+
+It is easy to understand how to fill the dropdown on edit and create forms: ABP makes an AJAX request to the given URL. It re-requests whenever user types to filter the items.
+
+However, for the data table, multiple items are shown on the UI and performing a separate AJAX call to get display name of the department for each row would not be so efficient.
+
+Instead, the display name of the foreign entity is also saved as an extra property of the entity (see *Extra Properties* section of the [Entities](Entities.md) document) in addition to Id of the foreign entity. If you check the database, you can see the `DepartmentId_Text` in the `ExtraProperties` field in the database table:
+
+````json
+{"DepartmentId":"21c7b61f-330c-489e-8b8c-80e0a78a5cc5","DepartmentId_Text":"Production"}
+````
+
+So, this is a type of *data duplication*. If your target entity's name changes in the database later, there is no automatic synchronization system. The system works as expected, but you see the old name on the data tables. If that's a problem for you, you should care yourself to update this information when display name of your entity changes.
+
## Database Mapping
For relational databases, all extension property values are stored in a single field in the table:
diff --git a/docs/en/Object-Extensions.md b/docs/en/Object-Extensions.md
index 308be4bfa7..72265b2690 100644
--- a/docs/en/Object-Extensions.md
+++ b/docs/en/Object-Extensions.md
@@ -410,3 +410,7 @@ ObjectExtensionManager.Instance
````
See the [Entity Framework Core Integration document](Entity-Framework-Core.md) for more.
+
+## See Also
+
+* [Module Entity Extensions](Module-Entity-Extensions.md)
\ No newline at end of file
diff --git a/docs/en/UI/AspNetCore/Branding.md b/docs/en/UI/AspNetCore/Branding.md
index e4a9ee0f3b..aa1500401d 100644
--- a/docs/en/UI/AspNetCore/Branding.md
+++ b/docs/en/UI/AspNetCore/Branding.md
@@ -20,13 +20,15 @@ namespace MyProject.Web
public class MyProjectBrandingProvider : DefaultBrandingProvider
{
public override string AppName => "Book Store";
+
+ public override string LogoUrl => "logo.png";
}
}
````
The result will be like shown below:
-
+
`IBrandingProvider` has the following properties:
@@ -38,8 +40,4 @@ The result will be like shown below:
## Overriding the Branding Area
-The [Basic Theme](Basic-Theme.md) doesn't implement the logos. However, you can see the [UI Customization Guide](Customization-User-Interface.md) to learn how you can replace the branding area with a custom view component.
-
-An example screenshot with an image is used in the branding area:
-
-
\ No newline at end of file
+You can see the [UI Customization Guide](Customization-User-Interface.md) to learn how you can replace the branding area with a custom view component.
diff --git a/docs/en/UI/Blazor/Theming.md b/docs/en/UI/Blazor/Theming.md
index cbc6341dab..00bf911ff9 100644
--- a/docs/en/UI/Blazor/Theming.md
+++ b/docs/en/UI/Blazor/Theming.md
@@ -63,7 +63,7 @@ A theme is simply a Razor Class Library.
### The Easy Way
-The easiest way to create a new theme is to copy the [Basic Theme Source Code](https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme) and customize it. Once you get a copy of the theme in your solution, remove the `Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic` NuGet package and reference to the local project.
+The easiest way to create a new theme is to copy the [Basic Theme Source Code](https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme) and customize it. Once you get a copy of the theme in your solution, remove the `Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme` NuGet package and reference to the local project.
### Global Styles / Scripts
diff --git a/docs/en/images/extension-navigation-property-form.png b/docs/en/images/extension-navigation-property-form.png
new file mode 100644
index 0000000000..5e55ce3afd
Binary files /dev/null and b/docs/en/images/extension-navigation-property-form.png differ
diff --git a/docs/en/images/extension-navigation-property-table.png b/docs/en/images/extension-navigation-property-table.png
new file mode 100644
index 0000000000..a3f333919c
Binary files /dev/null and b/docs/en/images/extension-navigation-property-table.png differ
diff --git a/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md b/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md
index 8a6ec1de9b..db434ab365 100644
--- a/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md
+++ b/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md
@@ -1920,7 +1920,7 @@ public class OrganizationAppService : ApplicationService
你可能想知道为什么付款逻辑代码不在`OrganizationManager`中.付款是非常**重要的事情**,我们不能**遗漏任何一次付款**.
-它确实非常重要,但是,它不能放到领域服务中.我们可能还又**其它用例**来创建组织但不收取任何费用.例如:
+它确实非常重要,但是,它不能放到领域服务中.我们可能还有**其它用例**来创建组织但不收取任何费用.例如:
* 管理员可以在后台管理系统创建新组织,而无需支付任何费用.
* 后台作业系统导入,集成,同步组织而无需支付费用.
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
index 3e60d5f9c2..a4d32550f8 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
@@ -55,7 +55,8 @@ $.validator.defaults.ignore = ''; //TODO: Would be better if we can apply only f
function _createContainer() {
_removeContainer();
- _$modalContainer = $('').appendTo('body');
+ _$modalContainer = $('');
+ $('body').prepend(_$modalContainer);
return _$modalContainer;
}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
index 75d2651cbc..3bd8aecbc1 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
@@ -16,6 +17,7 @@ using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Templates.App;
using Volo.Abp.Cli.ProjectBuilding.Templates.Console;
+using Volo.Abp.Cli.ProjectModification;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
@@ -24,14 +26,17 @@ namespace Volo.Abp.Cli.Commands
{
public class NewCommand : IConsoleCommand, ITransientDependency
{
+ private readonly EfCoreMigrationManager _efCoreMigrationManager;
public ILogger Logger { get; set; }
protected TemplateProjectBuilder TemplateProjectBuilder { get; }
public ITemplateInfoProvider TemplateInfoProvider { get; }
public NewCommand(TemplateProjectBuilder templateProjectBuilder
- , ITemplateInfoProvider templateInfoProvider)
+ , ITemplateInfoProvider templateInfoProvider,
+ EfCoreMigrationManager efCoreMigrationManager)
{
+ _efCoreMigrationManager = efCoreMigrationManager;
TemplateProjectBuilder = templateProjectBuilder;
TemplateInfoProvider = templateInfoProvider;
@@ -84,18 +89,24 @@ namespace Volo.Abp.Cli.Commands
Logger.LogInformation("Database provider: " + databaseProvider);
}
- var uiFramework = GetUiFramework(commandLineArgs);
- if (uiFramework != UiFramework.NotSpecified)
- {
- Logger.LogInformation("UI Framework: " + uiFramework);
- }
-
var connectionString = GetConnectionString(commandLineArgs);
if (connectionString != null)
{
Logger.LogInformation("Connection string: " + connectionString);
}
+ var databaseManagementSystem = GetDatabaseManagementSystem(commandLineArgs);
+ if (databaseManagementSystem != DatabaseManagementSystem.NotSpecified)
+ {
+ Logger.LogInformation("DBMS: " + databaseManagementSystem);
+ }
+
+ var uiFramework = GetUiFramework(commandLineArgs);
+ if (uiFramework != UiFramework.NotSpecified)
+ {
+ Logger.LogInformation("UI Framework: " + uiFramework);
+ }
+
var mobileApp = GetMobilePreference(commandLineArgs);
if (mobileApp != MobileApp.None)
{
@@ -147,6 +158,7 @@ namespace Volo.Abp.Cli.Commands
template,
version,
databaseProvider,
+ databaseManagementSystem,
uiFramework,
mobileApp,
gitHubAbpLocalRepositoryPath,
@@ -196,6 +208,8 @@ namespace Volo.Abp.Cli.Commands
}
}
+ DeleteMigrationsIfNeeded(databaseProvider, databaseManagementSystem, outputFolder);
+
Logger.LogInformation($"'{projectName}' has been successfully created to '{outputFolder}'");
if (AppTemplateBase.IsAppTemplate(template ?? (await TemplateInfoProvider.GetDefaultAsync()).Name))
@@ -205,9 +219,27 @@ namespace Volo.Abp.Cli.Commands
}
}
+ private void DeleteMigrationsIfNeeded(DatabaseProvider databaseProvider, DatabaseManagementSystem databaseManagementSystem, string outputFolder)
+ {
+ if (databaseManagementSystem == DatabaseManagementSystem.NotSpecified || databaseManagementSystem == DatabaseManagementSystem.SQLServer)
+ {
+ return;
+ }
+
+ if (databaseProvider != DatabaseProvider.NotSpecified && databaseProvider != DatabaseProvider.EntityFrameworkCore)
+ {
+ return;
+ }
+
+ Logger.LogInformation($"Deleting migrations...");
+
+ _efCoreMigrationManager.RemoveAllMigrations(outputFolder);
+ }
+
private void OpenThanksPage(UiFramework uiFramework, DatabaseProvider databaseProvider, bool tiered, bool commercial)
{
uiFramework = uiFramework == UiFramework.NotSpecified || uiFramework == UiFramework.None ? UiFramework.Mvc : uiFramework;
+ databaseProvider = databaseProvider == DatabaseProvider.NotSpecified ? DatabaseProvider.EntityFrameworkCore : databaseProvider;
var urlPrefix = commercial ? "commercial" : "www";
var tieredYesNo = tiered ? "yes" : "no";
@@ -267,6 +299,7 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine("-ts|--template-source (your local or network abp template source)");
sb.AppendLine("-csf|--create-solution-folder (default: true)");
sb.AppendLine("-cs|--connection-string (your database connection string)");
+ sb.AppendLine("--dbms (your database management system)");
sb.AppendLine("--tiered (if supported by the template)");
sb.AppendLine("--no-ui (if supported by the template)");
sb.AppendLine("--no-random-port (Use template's default ports)");
@@ -289,6 +322,7 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine(" abp new Acme.BookStore -ts \"D:\\localTemplate\\abp\"");
sb.AppendLine(" abp new Acme.BookStore -csf false");
sb.AppendLine(" abp new Acme.BookStore --local-framework-ref --abp-path \"D:\\github\\abp\"");
+ sb.AppendLine(" abp new Acme.BookStore --dbms mysql");
sb.AppendLine(" abp new Acme.BookStore --connection-string \"Server=myServerName\\myInstanceName;Database=myDatabase;User Id=myUsername;Password=myPassword\"");
sb.AppendLine("");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
@@ -315,6 +349,34 @@ namespace Volo.Abp.Cli.Commands
}
}
+ protected virtual DatabaseManagementSystem GetDatabaseManagementSystem(CommandLineArgs commandLineArgs)
+ {
+ var optionValue = commandLineArgs.Options.GetOrNull(Options.DatabaseManagementSystem.Short, Options.DatabaseManagementSystem.Long);
+
+ if (optionValue == null)
+ {
+ return DatabaseManagementSystem.NotSpecified;
+ }
+
+ switch (optionValue.ToLowerInvariant())
+ {
+ case "sqlserver":
+ return DatabaseManagementSystem.SQLServer;
+ case "mysql":
+ return DatabaseManagementSystem.MySQL;
+ case "postgresql":
+ return DatabaseManagementSystem.PostgreSQL;
+ case "oracle-devart":
+ return DatabaseManagementSystem.OracleDevart;
+ case "sqlite":
+ return DatabaseManagementSystem.SQLite;
+ case "oracle": // Currently disabled. See https://github.com/abpframework/abp/issues/6513
+ // return DatabaseManagementSystem.Oracle;
+ default:
+ return DatabaseManagementSystem.NotSpecified;
+ }
+ }
+
protected virtual UiFramework GetUiFramework(CommandLineArgs commandLineArgs)
{
var optionValue = commandLineArgs.Options.GetOrNull(Options.UiFramework.Short, Options.UiFramework.Long);
@@ -362,6 +424,12 @@ namespace Volo.Abp.Cli.Commands
public const string Long = "database-provider";
}
+ public static class DatabaseManagementSystem
+ {
+ public const string Short = "dbms";
+ public const string Long = "database-management-system";
+ }
+
public static class OutputFolder
{
public const string Short = "o";
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs
index 78a95996bd..28f41e55d8 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs
@@ -37,6 +37,7 @@ namespace Volo.Abp.Cli.Commands.Services
moduleName,
version,
DatabaseProvider.NotSpecified,
+ DatabaseManagementSystem.NotSpecified,
UiFramework.NotSpecified,
null,
gitHubAbpLocalRepositoryPath,
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseManagementSystem.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseManagementSystem.cs
new file mode 100644
index 0000000000..fcb3130854
--- /dev/null
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseManagementSystem.cs
@@ -0,0 +1,13 @@
+namespace Volo.Abp.Cli.ProjectBuilding.Building
+{
+ public enum DatabaseManagementSystem
+ {
+ NotSpecified,
+ SQLServer,
+ MySQL,
+ PostgreSQL,
+ Oracle,
+ OracleDevart,
+ SQLite
+ }
+}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs
new file mode 100644
index 0000000000..4286710899
--- /dev/null
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Linq;
+using Volo.Abp.Cli.ProjectBuilding.Files;
+
+namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
+{
+ public class DatabaseManagementSystemChangeStep : ProjectBuildPipelineStep
+ {
+ public override void Execute(ProjectBuildContext context)
+ {
+ switch (context.BuildArgs.DatabaseManagementSystem)
+ {
+ case DatabaseManagementSystem.MySQL:
+ ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.MySQL",
+ "Volo.Abp.EntityFrameworkCore.MySQL",
+ "AbpEntityFrameworkCoreMySQLModule");
+ AddMySqlServerVersion(context);
+ ChangeUseSqlServer(context,"UseMySQL", "UseMySql");
+ break;
+
+ case DatabaseManagementSystem.PostgreSQL:
+ ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.PostgreSql",
+ "Volo.Abp.EntityFrameworkCore.PostgreSql",
+ "AbpEntityFrameworkCorePostgreSqlModule");
+ ChangeUseSqlServer(context,"UseNpgsql");
+ break;
+
+ case DatabaseManagementSystem.Oracle:
+ ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.Oracle",
+ "Volo.Abp.EntityFrameworkCore.Oracle",
+ "AbpEntityFrameworkCoreOracleModule");
+ ChangeUseSqlServer(context,"UseOracle");
+ break;
+
+ case DatabaseManagementSystem.OracleDevart:
+ ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.Oracle.Devart",
+ "Volo.Abp.EntityFrameworkCore.Oracle.Devart",
+ "AbpEntityFrameworkCoreOracleDevartModule");
+ AdjustOracleDbContextOptionsBuilder(context);
+ ChangeUseSqlServer(context,"UseOracle");
+ break;
+
+ case DatabaseManagementSystem.SQLite:
+ ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.Sqlite",
+ "Volo.Abp.EntityFrameworkCore.Sqlite",
+ "AbpEntityFrameworkCoreSqliteModule");
+ ChangeUseSqlServer(context,"UseSqlite");
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ private void AdjustOracleDbContextOptionsBuilder(ProjectBuildContext context)
+ {
+ var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase));
+
+ dbContextFactoryFile.ReplaceText("new DbContextOptionsBuilder",
+ $"(DbContextOptionsBuilder<{context.BuildArgs.SolutionName.ProjectName}MigrationsDbContext>) new DbContextOptionsBuilder");
+ }
+
+ private void AddMySqlServerVersion(ProjectBuildContext context)
+ {
+ var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase));
+
+ dbContextFactoryFile.ReplaceText("configuration.GetConnectionString(\"Default\")",
+ "configuration.GetConnectionString(\"Default\"), MySqlServerVersion.LatestSupportedServerVersion");
+ }
+
+ private void ChangeEntityFrameworkCoreDependency(ProjectBuildContext context, string newPackageName, string newModuleNamespace, string newModuleClass)
+ {
+ var efCoreProjectFile = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCore.csproj", StringComparison.OrdinalIgnoreCase));
+ efCoreProjectFile.ReplaceText("Volo.Abp.EntityFrameworkCore.SqlServer", newPackageName);
+
+ var efCoreModuleClass = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase));
+ efCoreModuleClass.ReplaceText("Volo.Abp.EntityFrameworkCore.SqlServer", newModuleNamespace);
+ efCoreModuleClass.ReplaceText("AbpEntityFrameworkCoreSqlServerModule", newModuleClass);
+ }
+
+ private void ChangeUseSqlServer(ProjectBuildContext context, string newUseMethodForEfModule, string newUseMethodForDbContext = null)
+ {
+ if (newUseMethodForDbContext == null)
+ {
+ newUseMethodForDbContext = newUseMethodForEfModule;
+ }
+
+ var oldUseMethod = "UseSqlServer";
+
+ var efCoreModuleClass = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase));
+ efCoreModuleClass.ReplaceText(oldUseMethod, newUseMethodForEfModule);
+
+ var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase));
+ dbContextFactoryFile.ReplaceText(oldUseMethod, newUseMethodForDbContext);
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs
index ad5da58a8e..9590001ea4 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs
@@ -25,6 +25,12 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building
pipeline.Steps.Add(new LicenseCodeReplaceStep());
}
+ if (context.Template.Name == AppTemplate.TemplateName ||
+ context.Template.Name == AppProTemplate.TemplateName)
+ {
+ pipeline.Steps.Add(new DatabaseManagementSystemChangeStep());
+ }
+
if ((context.BuildArgs.UiFramework == UiFramework.Mvc || context.BuildArgs.UiFramework == UiFramework.Blazor)
&& context.BuildArgs.MobileApp == MobileApp.None)
{
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs
index 52fa76e551..1022f5f10e 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs
@@ -17,6 +17,8 @@ namespace Volo.Abp.Cli.ProjectBuilding
public DatabaseProvider DatabaseProvider { get; set; }
+ public DatabaseManagementSystem DatabaseManagementSystem { get; set; }
+
public UiFramework UiFramework { get; set; }
public MobileApp? MobileApp { get; set; }
@@ -41,6 +43,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
[CanBeNull] string templateName = null,
[CanBeNull] string version = null,
DatabaseProvider databaseProvider = DatabaseProvider.NotSpecified,
+ DatabaseManagementSystem databaseManagementSystem = DatabaseManagementSystem.NotSpecified,
UiFramework uiFramework = UiFramework.NotSpecified,
MobileApp? mobileApp = null,
[CanBeNull] string abpGitHubLocalRepositoryPath = null,
@@ -53,6 +56,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
TemplateName = templateName;
Version = version;
DatabaseProvider = databaseProvider;
+ DatabaseManagementSystem = databaseManagementSystem;
UiFramework = uiFramework;
MobileApp = mobileApp;
AbpGitHubLocalRepositoryPath = abpGitHubLocalRepositoryPath;
@@ -62,4 +66,4 @@ namespace Volo.Abp.Cli.ProjectBuilding
ConnectionString = connectionString;
}
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
index a598d3483c..b5976b403f 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
@@ -24,6 +24,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
SwitchDatabaseProvider(context, steps);
DeleteUnrelatedProjects(context, steps);
+ RemoveUnnecessaryPorts(context, steps);
RandomizeSslPorts(context, steps);
RandomizeStringEncryption(context, steps);
UpdateNuGetConfig(context, steps);
@@ -102,6 +103,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
if (context.BuildArgs.ExtraProperties.ContainsKey("separate-identity-server"))
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds"));
+ steps.Add(new AppTemplateChangeDbMigratorPortSettingsStep("44300"));
}
else
{
@@ -122,6 +124,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds"));
steps.Add(new BlazorAppsettingsFilePortChangeForSeparatedIdentityServersStep());
+ steps.Add(new AppTemplateChangeDbMigratorPortSettingsStep("44300"));
}
else
{
@@ -139,6 +142,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Tests", projectFolderPath: "/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests"));
steps.Add(new AppTemplateProjectRenameStep("MyCompanyName.MyProjectName.Web.Host", "MyCompanyName.MyProjectName.Web"));
+ steps.Add(new AppTemplateChangeDbMigratorPortSettingsStep("44300"));
}
else
{
@@ -161,6 +165,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds"));
steps.Add(new AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep());
+ steps.Add(new AppTemplateChangeDbMigratorPortSettingsStep("44300"));
if (context.BuildArgs.MobileApp == MobileApp.ReactNative)
{
@@ -176,6 +181,11 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
}
}
+ private static void RemoveUnnecessaryPorts(ProjectBuildContext context, List steps)
+ {
+ steps.Add(new RemoveUnnecessaryPortsStep());
+ }
+
private static void RandomizeSslPorts(ProjectBuildContext context, List steps)
{
if (context.BuildArgs.ExtraProperties.ContainsKey("no-random-port"))
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateChangeDbMigratorPortSettingsStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateChangeDbMigratorPortSettingsStep.cs
new file mode 100644
index 0000000000..2d192f1478
--- /dev/null
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateChangeDbMigratorPortSettingsStep.cs
@@ -0,0 +1,24 @@
+using Volo.Abp.Cli.ProjectBuilding.Building;
+using Volo.Abp.Cli.ProjectBuilding.Files;
+
+namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
+{
+ public class AppTemplateChangeDbMigratorPortSettingsStep : ProjectBuildPipelineStep
+ {
+ public string IdentityServerPort { get; }
+
+ ///
+ public AppTemplateChangeDbMigratorPortSettingsStep(
+ string identityServerPort)
+ {
+ IdentityServerPort = identityServerPort;
+ }
+
+ public override void Execute(ProjectBuildContext context)
+ {
+ context
+ .GetFile("/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json")
+ .ReplaceText("44305", IdentityServerPort);
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RemoveUnnecessaryPortsStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RemoveUnnecessaryPortsStep.cs
new file mode 100644
index 0000000000..4d44cf0b05
--- /dev/null
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RemoveUnnecessaryPortsStep.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Volo.Abp.Cli.ProjectBuilding.Building;
+
+namespace Volo.Abp.Cli.ProjectBuilding.Templates
+{
+ public class RemoveUnnecessaryPortsStep : ProjectBuildPipelineStep
+ {
+ public override void Execute(ProjectBuildContext context)
+ {
+ var httpApiHostAppSettings = context.Files.FirstOrDefault(f => f.Name.EndsWith(".HttpApi.Host/appsettings.json"));
+
+ if (httpApiHostAppSettings == null)
+ {
+ return;
+ }
+
+ var portsToRemoveFromCors = new List();
+
+ var appSettingsJson = JObject.Parse(httpApiHostAppSettings.Content);
+ var appJson = (JObject) appSettingsJson["App"];
+
+ if (context.BuildArgs.UiFramework != UiFramework.Angular)
+ {
+ appJson.Property("ClientUrl")?.Remove();
+ portsToRemoveFromCors.Add("4200");
+ }
+ if (context.BuildArgs.UiFramework != UiFramework.Blazor)
+ {
+ portsToRemoveFromCors.Add("44307");
+ }
+
+ if (appJson["CorsOrigins"] != null)
+ {
+ appJson["CorsOrigins"] = string.Join(",",
+ appJson["CorsOrigins"].ToString().Split(",").Where(u=> !portsToRemoveFromCors.Any(u.EndsWith))
+ );
+ }
+
+ httpApiHostAppSettings.SetContent(JsonConvert.SerializeObject(appSettingsJson, Formatting.Indented));
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs
deleted file mode 100644
index 280d4b8f34..0000000000
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.IO;
-using Volo.Abp.Cli.Utils;
-using Volo.Abp.DependencyInjection;
-
-namespace Volo.Abp.Cli.ProjectModification
-{
- public class EfCoreMigrationAdder : ITransientDependency
- {
- public void AddMigration(string dbMigrationsCsprojFile, string module, string startupProject)
- {
- var moduleName = ParseModuleName(module);
- var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix();
-
- CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigrationsCsprojFile) + "\" && dotnet ef migrations add " + migrationName + GetStartupProjectOption(startupProject));
- }
-
- protected virtual string ParseModuleName(string fullModuleName)
- {
- var words = fullModuleName?.Split('.');
-
- if (words == null || words.Length <= 1)
- {
- return "";
- }
-
- return words[words.Length - 1];
- }
-
- protected virtual string GetUniquePostFix()
- {
- return "_" + new Random().Next(1,99999);
- }
-
- protected virtual string GetStartupProjectOption(string startupProject)
- {
- return startupProject.IsNullOrWhiteSpace() ? "" : $" -s {startupProject}";
- }
- }
-}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs
new file mode 100644
index 0000000000..b26ca1be90
--- /dev/null
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs
@@ -0,0 +1,73 @@
+using System;
+using System.IO;
+using System.Linq;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Volo.Abp.Cli.Utils;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Cli.ProjectModification
+{
+ public class EfCoreMigrationManager : ITransientDependency
+ {
+ public ILogger Logger { get; set; }
+
+ public EfCoreMigrationManager()
+ {
+ Logger = NullLogger.Instance;
+ }
+
+ public void AddMigration(string dbMigrationsCsprojFile, string module, string startupProject)
+ {
+ var moduleName = ParseModuleName(module);
+ var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix();
+
+ CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigrationsCsprojFile) +
+ "\" && dotnet ef migrations add " + migrationName +
+ GetStartupProjectOption(startupProject));
+ }
+
+ public void RemoveAllMigrations(string solutionFolder)
+ {
+ if (Directory.Exists(Path.Combine(solutionFolder, "aspnet-core")))
+ {
+ solutionFolder = Path.Combine(solutionFolder, "aspnet-core");
+ }
+
+ var srcFolder = Path.Combine(solutionFolder, "src");
+
+ var migrationsFolder = Directory.GetDirectories(srcFolder)
+ .FirstOrDefault(d => d.EndsWith(".EntityFrameworkCore.DbMigrations"));
+
+ if (migrationsFolder != null)
+ {
+ Directory.Delete(Path.Combine(migrationsFolder, "Migrations"), true);
+ }
+ else
+ {
+ Logger.LogWarning("No migration found to delete.");
+ }
+ }
+
+ protected virtual string ParseModuleName(string fullModuleName)
+ {
+ var words = fullModuleName?.Split('.');
+ if (words == null || words.Length <= 1)
+ {
+ return "";
+ }
+
+ return words[words.Length - 1];
+ }
+
+ protected virtual string GetUniquePostFix()
+ {
+ return "_" + new Random().Next(1, 99999);
+ }
+
+ protected virtual string GetStartupProjectOption(string startupProject)
+ {
+ return startupProject.IsNullOrWhiteSpace() ? "" : $" -s {startupProject}";
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
index 4eae8ace8f..07896b49a1 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
@@ -26,7 +26,7 @@ namespace Volo.Abp.Cli.ProjectModification
protected IJsonSerializer JsonSerializer { get; }
protected ProjectNugetPackageAdder ProjectNugetPackageAdder { get; }
protected DbContextFileBuilderConfigureAdder DbContextFileBuilderConfigureAdder { get; }
- protected EfCoreMigrationAdder EfCoreMigrationAdder { get; }
+ protected EfCoreMigrationManager EfCoreMigrationManager { get; }
protected DerivedClassFinder DerivedClassFinder { get; }
protected ProjectNpmPackageAdder ProjectNpmPackageAdder { get; }
protected NpmGlobalPackagesChecker NpmGlobalPackagesChecker { get; }
@@ -42,7 +42,7 @@ namespace Volo.Abp.Cli.ProjectModification
IJsonSerializer jsonSerializer,
ProjectNugetPackageAdder projectNugetPackageAdder,
DbContextFileBuilderConfigureAdder dbContextFileBuilderConfigureAdder,
- EfCoreMigrationAdder efCoreMigrationAdder,
+ EfCoreMigrationManager efCoreMigrationManager,
DerivedClassFinder derivedClassFinder,
ProjectNpmPackageAdder projectNpmPackageAdder,
NpmGlobalPackagesChecker npmGlobalPackagesChecker,
@@ -57,7 +57,7 @@ namespace Volo.Abp.Cli.ProjectModification
JsonSerializer = jsonSerializer;
ProjectNugetPackageAdder = projectNugetPackageAdder;
DbContextFileBuilderConfigureAdder = dbContextFileBuilderConfigureAdder;
- EfCoreMigrationAdder = efCoreMigrationAdder;
+ EfCoreMigrationManager = efCoreMigrationManager;
DerivedClassFinder = derivedClassFinder;
ProjectNpmPackageAdder = projectNpmPackageAdder;
NpmGlobalPackagesChecker = npmGlobalPackagesChecker;
@@ -454,7 +454,7 @@ namespace Volo.Abp.Cli.ProjectModification
{
if (addedNewBuilder)
{
- EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject);
+ EfCoreMigrationManager.AddMigration(dbMigrationsProject, module.Name, startupProject);
}
RunMigrator(projectFiles);
diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de-DE.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de-DE.json
index 714e7f69c2..2ae7c2c53f 100644
--- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de-DE.json
+++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de-DE.json
@@ -57,6 +57,9 @@
"ProfileTab:Password": "Passwort ändern",
"ProfileTab:PersonalInfo": "Persönliche Informationen",
"ReturnToApplication": "Zur Anwendung zurückkehren",
- "Volo.Account:InvalidEmailAddress": "Die angegebene E-Mail-Adresse kann nicht gefunden werden: {0}"
+ "Volo.Account:InvalidEmailAddress": "Die angegebene E-Mail-Adresse kann nicht gefunden werden: {0}",
+ "PasswordReset": "Passwort zurücksetzen",
+ "PasswordResetInfoInEmail": "Wir haben eine Anfrage zur Wiederherstellung des Kontos erhalten! Wenn Sie diese Anforderung initiiert haben, klicken Sie auf den folgenden Link, um Ihr Passwort zurückzusetzen.",
+ "ResetMyPassword": "Mein Passwort zurücksetzen"
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json
index e7cdc64c43..755cd5674b 100644
--- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json
+++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json
@@ -57,6 +57,9 @@
"ProfileTab:Password": "Change password",
"ProfileTab:PersonalInfo": "Personal info",
"ReturnToApplication": "Return to application",
- "Volo.Account:InvalidEmailAddress": "Can not find the given email address: {0}"
+ "Volo.Account:InvalidEmailAddress": "Can not find the given email address: {0}",
+ "PasswordReset": "Password reset",
+ "PasswordResetInfoInEmail": "We received an account recovery request! If you initiated this request, click the following link to reset your password.",
+ "ResetMyPassword": "Reset my password"
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json
index 5f7d824ca0..f451cff366 100644
--- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json
+++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json
@@ -57,6 +57,9 @@
"ProfileTab:Password": "Cambiar contraseña",
"ProfileTab:PersonalInfo": "Información personal",
"ReturnToApplication": "Volver a la aplicación",
- "Volo.Account:InvalidEmailAddress": "No se puede encontrar la dirección de e-mail solicitada: {0}"
+ "Volo.Account:InvalidEmailAddress": "No se puede encontrar la dirección de e-mail solicitada: {0}",
+ "PasswordReset": "Restablecer contraseña",
+ "PasswordResetInfoInEmail": "Recibimos una solicitud de recuperación de cuenta. Si inició esta solicitud, haga clic en el siguiente enlace para restablecer su contraseña.",
+ "ResetMyPassword": "Restablecer mi contraseña"
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json
index 4cd3d801fc..13a8fd4848 100644
--- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json
+++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json
@@ -49,6 +49,9 @@
"ResetPassword_Information": "Prosimo vnesite vaše novo geslo.",
"YourPasswordIsSuccessfullyReset": "Vaše geslo je uspešno ponastavljeno.",
"BackToLogin": "Nazaj na prijavo",
- "Volo.Account:InvalidEmailAddress": "Navedenega e-poštnega naslova ni mogoče najti: {0}"
+ "Volo.Account:InvalidEmailAddress": "Navedenega e-poštnega naslova ni mogoče najti: {0}",
+ "PasswordReset": "Ponastavitev gesla",
+ "PasswordResetInfoInEmail": "Prejeta je bila zahteva za obnovitev računa! V kolikor ste vi sprožili zahtevo, kliknite na sledečo povezavo, da ponastavite geslo.",
+ "ResetMyPassword": "Ponastavi geslo"
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json
index 3be2997f68..d8cf5dd74f 100644
--- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json
+++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json
@@ -57,6 +57,9 @@
"ProfileTab:Password": "Şifre değiştir",
"ProfileTab:PersonalInfo": "Kişisel bilgiler",
"ReturnToApplication": "Uygulamaya geri dön",
- "Volo.Account:InvalidEmailAddress": "Email adresi bulunamadı: {0}"
+ "Volo.Account:InvalidEmailAddress": "Email adresi bulunamadı: {0}",
+ "PasswordReset": "Şifre Sıfırlama",
+ "PasswordResetInfoInEmail": "Şifrenizi sıfırlamanız için bir talep aldık! Eğer bu talebi siz gerçekleştirmişseniz, şifrenizi sıfırlamak için bağlantıya tıklayın.",
+ "ResetMyPassword": "Şifremi sıfırla"
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json
index f5995c6e28..53fe6135f2 100644
--- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json
+++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json
@@ -57,6 +57,9 @@
"ProfileTab:Password": "更改密码",
"ProfileTab:PersonalInfo": "个人信息",
"ReturnToApplication": "返回到应用程序",
- "Volo.Account:InvalidEmailAddress": "找不到给定的电子邮件地址:{0}"
+ "Volo.Account:InvalidEmailAddress": "找不到给定的电子邮件地址:{0}",
+ "PasswordReset": "重设密码",
+ "PasswordResetInfoInEmail": "我们收到了帐户恢复请求!如果你发起了此请求,请单击以下链接以重置密码.",
+ "ResetMyPassword": "重置我的密码"
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json
index f4fd2d65b1..a67ea8d63f 100644
--- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json
+++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json
@@ -55,6 +55,9 @@
"BackToLogin": "返回登錄",
"ProfileTab:Password": "更改密碼",
"ProfileTab:PersonalInfo": "個人信息",
- "ReturnToApplication": "返回到應用程序"
+ "ReturnToApplication": "返回到應用程序",
+ "PasswordReset": "重設密碼",
+ "PasswordResetInfoInEmail": "我們收到了帳戶恢復請求!如果你發起了此請求,請單擊以下鏈接以重置密碼.",
+ "ResetMyPassword": "重置我的密碼"
}
}
diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/AbpVirtualFileExplorerWebModule.cs b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/AbpVirtualFileExplorerWebModule.cs
index f49628aa45..41c007e06d 100644
--- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/AbpVirtualFileExplorerWebModule.cs
+++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/AbpVirtualFileExplorerWebModule.cs
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.FileProviders.Physical;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Prismjs;
diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Pages/VirtualFileExplorer/Index.cshtml.cs b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Pages/VirtualFileExplorer/Index.cshtml.cs
index b450531cc8..14f9fc581f 100644
--- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Pages/VirtualFileExplorer/Index.cshtml.cs
+++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Pages/VirtualFileExplorer/Index.cshtml.cs
@@ -1,10 +1,9 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.FileProviders;
-using NUglify.Helpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination;
using Volo.Abp.VirtualFileExplorer.Web.Models;
using Volo.Abp.VirtualFileSystem;
@@ -31,7 +30,6 @@ namespace Volo.Abp.VirtualFileExplorer.Web.Pages.VirtualFileExplorer
protected IVirtualFileProvider VirtualFileProvider { get; }
-
public IndexModel(IVirtualFileProvider virtualFileProvider)
{
VirtualFileProvider = virtualFileProvider;
@@ -67,16 +65,26 @@ namespace Volo.Abp.VirtualFileExplorer.Web.Pages.VirtualFileExplorer
LastUpdateTime = fileInfo.LastModified.LocalDateTime
};
+ var filePath = fileInfo.PhysicalPath ?? $"{Path.EnsureEndsWith('/')}{fileInfo.Name}";;
+
if (fileInfo.IsDirectory)
{
fileInfoViewModel.Icon = "fas fa-folder";
fileInfoViewModel.FileType = "folder";
fileInfoViewModel.Length = "/";
- fileInfoViewModel.FileName =$"{fileInfo.Name}";
+ fileInfoViewModel.FileName =$"{fileInfo.Name}";
}
else
{
- fileInfoViewModel.FilePath = ((EmbeddedResourceFileInfo) fileInfo).VirtualPath;
+ if (fileInfo is EmbeddedResourceFileInfo embeddedResourceFileInfo)
+ {
+ fileInfoViewModel.FilePath = embeddedResourceFileInfo.VirtualPath;
+ }
+ else
+ {
+ fileInfoViewModel.FilePath = filePath;
+ }
+
}
FileInfoList.Add(fileInfoViewModel);
diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/VirtualFileExplorerConsts.cs b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/VirtualFileExplorerConsts.cs
index a05020b764..08a5916a0a 100644
--- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/VirtualFileExplorerConsts.cs
+++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/VirtualFileExplorerConsts.cs
@@ -2,6 +2,6 @@
{
public static class VirtualFileExplorerConsts
{
- public static string[] AllowFileInfoTypes = new[] {"VirtualDirectoryFileInfo", "EmbeddedResourceFileInfo"};
+ public static readonly string[] AllowFileInfoTypes = {"VirtualDirectoryFileInfo", "EmbeddedResourceFileInfo", "ManifestDirectoryInfo" , "ManifestFileInfo"};
}
}
diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj
index d41cec81c1..cfe69554ac 100644
--- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj
+++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj
@@ -9,6 +9,7 @@
false
false
false
+ true
true
Library
@@ -18,14 +19,12 @@
-
-
@@ -34,4 +33,8 @@
+
+
+
+
diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts
index a661012ac7..f25f8f07ac 100644
--- a/npm/ng-packs/packages/core/src/lib/core.module.ts
+++ b/npm/ng-packs/packages/core/src/lib/core.module.ts
@@ -52,6 +52,7 @@ export function storageFactory(): OAuthStorage {
FormsModule,
ReactiveFormsModule,
RouterModule,
+ LocalizationModule,
AbstractNgModelComponent,
AutofocusDirective,
@@ -76,6 +77,7 @@ export function storageFactory(): OAuthStorage {
FormsModule,
ReactiveFormsModule,
RouterModule,
+ LocalizationModule,
],
declarations: [
AbstractNgModelComponent,
@@ -94,6 +96,7 @@ export function storageFactory(): OAuthStorage {
StopPropagationDirective,
VisibilityDirective,
],
+ providers: [LocalizationPipe],
entryComponents: [
RouterOutletComponent,
DynamicLayoutComponent,
@@ -125,9 +128,8 @@ export class RootCoreModule {}
* CoreModule is the module that is publicly available
*/
@NgModule({
- exports: [BaseCoreModule, LocalizationModule],
- imports: [BaseCoreModule, LocalizationModule],
- providers: [LocalizationPipe],
+ exports: [BaseCoreModule],
+ imports: [BaseCoreModule],
})
export class CoreModule {
static forRoot(options = {} as ABP.Root): ModuleWithProviders {
diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts
index a2b08c07db..ad60178a98 100644
--- a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts
+++ b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts
@@ -1,10 +1,9 @@
import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
-import { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { finalize } from 'rxjs/operators';
-import { StartLoader, StopLoader } from '../actions/loader.actions';
import { SessionStateService } from '../services/session-state.service';
+import { HttpWaitService } from '../services/http-wait.service';
@Injectable({
providedIn: 'root',
@@ -12,20 +11,19 @@ import { SessionStateService } from '../services/session-state.service';
export class ApiInterceptor implements HttpInterceptor {
constructor(
private oAuthService: OAuthService,
- private store: Store,
private sessionState: SessionStateService,
+ private httpWaitService: HttpWaitService,
) {}
intercept(request: HttpRequest, next: HttpHandler) {
- this.store.dispatch(new StartLoader(request));
-
+ this.httpWaitService.addRequest(request);
return next
.handle(
request.clone({
setHeaders: this.getAdditionalHeaders(request.headers),
}),
)
- .pipe(finalize(() => this.store.dispatch(new StopLoader(request))));
+ .pipe(finalize(() => this.httpWaitService.deleteRequest(request)));
}
getAdditionalHeaders(existingHeaders?: HttpHeaders) {
diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts
index 88bd75c027..d4d44faae0 100644
--- a/npm/ng-packs/packages/core/src/lib/models/common.ts
+++ b/npm/ng-packs/packages/core/src/lib/models/common.ts
@@ -1,5 +1,5 @@
import { EventEmitter, Type } from '@angular/core';
-import { Router, Routes } from '@angular/router';
+import { Routes } from '@angular/router';
import { Subject } from 'rxjs';
import { eLayoutType } from '../enums/common';
import { Environment } from './environment';
@@ -13,7 +13,8 @@ export namespace ABP {
}
export interface Test extends Partial {
- baseHref?: Router;
+ baseHref?: string;
+ listQueryDebounceTime?: number;
routes?: Routes;
}
diff --git a/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts b/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts
new file mode 100644
index 0000000000..96427594ae
--- /dev/null
+++ b/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts
@@ -0,0 +1,44 @@
+import { Injectable } from '@angular/core';
+import { HttpRequest } from '@angular/common/http';
+import { InternalStore } from '../utils/internal-store-utils';
+
+export interface HttpWaitState {
+ requests: Set>;
+}
+
+@Injectable({
+ providedIn: 'root',
+})
+export class HttpWaitService {
+ protected store = new InternalStore({ requests: new Set() });
+
+ getLoading() {
+ return !!this.store.state.requests.size;
+ }
+
+ getLoading$() {
+ return this.store.sliceState(({ requests }) => !!requests.size);
+ }
+
+ updateLoading$() {
+ return this.store.sliceUpdate(({ requests }) => !!requests.size);
+ }
+
+ clearLoading() {
+ this.store.patch({ requests: new Set() });
+ }
+
+ addRequest(request: HttpRequest) {
+ const requests = this.store.state.requests;
+ requests.add(request);
+ this.store.patch({ requests });
+ }
+
+ deleteRequest(request: HttpRequest) {
+ const requests = this.store.state.requests;
+ requests.delete(request);
+ this.store.patch({ requests });
+ }
+
+ // TODO: Add filter function
+}
diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts
index 5b294c4905..37bc78d606 100644
--- a/npm/ng-packs/packages/core/src/lib/services/index.ts
+++ b/npm/ng-packs/packages/core/src/lib/services/index.ts
@@ -4,6 +4,7 @@ export * from './config-state.service';
export * from './content-projection.service';
export * from './dom-insertion.service';
export * from './environment.service';
+export * from './http-wait.service';
export * from './lazy-load.service';
export * from './list.service';
export * from './localization.service';
@@ -12,7 +13,9 @@ export * from './permission.service';
export * from './profile-state.service';
export * from './profile.service';
export * from './replaceable-components.service';
+export * from './resource-wait.service';
export * from './rest.service';
+export * from './router-wait.service';
export * from './routes.service';
export * from './session-state.service';
export * from './subscription.service';
diff --git a/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts b/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts
index 5d44bb0879..0aca94c94a 100644
--- a/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts
+++ b/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { concat, Observable, of, throwError } from 'rxjs';
import { delay, retryWhen, shareReplay, take, tap } from 'rxjs/operators';
import { LoadingStrategy } from '../strategies';
+import { ResourceWaitService } from './resource-wait.service';
@Injectable({
providedIn: 'root',
@@ -9,9 +10,11 @@ import { LoadingStrategy } from '../strategies';
export class LazyLoadService {
readonly loaded = new Map();
+ constructor(private resourceWaitService: ResourceWaitService) {}
+
load(strategy: LoadingStrategy, retryTimes?: number, retryDelay?: number): Observable {
if (this.loaded.has(strategy.path)) return of(new CustomEvent('load'));
-
+ this.resourceWaitService.addResource(strategy.path);
return strategy.createStream().pipe(
retryWhen(error$ =>
concat(
@@ -19,7 +22,10 @@ export class LazyLoadService {
throwError(new CustomEvent('error')),
),
),
- tap(() => this.loaded.set(strategy.path, strategy.element)),
+ tap(() => {
+ this.loaded.set(strategy.path, strategy.element);
+ this.resourceWaitService.deleteResource(strategy.path);
+ }),
delay(100),
shareReplay({ bufferSize: 1, refCount: true }),
);
diff --git a/npm/ng-packs/packages/core/src/lib/services/list.service.ts b/npm/ng-packs/packages/core/src/lib/services/list.service.ts
index 135b13a912..0bd3e927ae 100644
--- a/npm/ng-packs/packages/core/src/lib/services/list.service.ts
+++ b/npm/ng-packs/packages/core/src/lib/services/list.service.ts
@@ -1,5 +1,12 @@
-import { Inject, Injectable, OnDestroy, Optional } from '@angular/core';
-import { BehaviorSubject, Observable, of, ReplaySubject, Subject } from 'rxjs';
+import { Injectable, Injector, OnDestroy } from '@angular/core';
+import {
+ BehaviorSubject,
+ MonoTypeOperatorFunction,
+ Observable,
+ of,
+ ReplaySubject,
+ Subject,
+} from 'rxjs';
import {
catchError,
debounceTime,
@@ -68,7 +75,7 @@ export class ListService implements OnDes
get query$(): Observable {
return this._query$
.asObservable()
- .pipe(debounceTime(this.delay || 300), shareReplay({ bufferSize: 1, refCount: true }));
+ .pipe(this.delay, shareReplay({ bufferSize: 1, refCount: true }));
}
private _isLoading$ = new BehaviorSubject(false);
@@ -89,7 +96,11 @@ export class ListService implements OnDes
} as any) as QueryParamsType);
};
- constructor(@Optional() @Inject(LIST_QUERY_DEBOUNCE_TIME) private delay: number) {
+ private delay: MonoTypeOperatorFunction;
+
+ constructor(injector: Injector) {
+ const delay = injector.get(LIST_QUERY_DEBOUNCE_TIME, 300);
+ this.delay = delay ? debounceTime(delay) : tap();
this.get();
}
diff --git a/npm/ng-packs/packages/core/src/lib/services/resource-wait.service.ts b/npm/ng-packs/packages/core/src/lib/services/resource-wait.service.ts
new file mode 100644
index 0000000000..16351837a2
--- /dev/null
+++ b/npm/ng-packs/packages/core/src/lib/services/resource-wait.service.ts
@@ -0,0 +1,41 @@
+import { Injectable } from '@angular/core';
+import { InternalStore } from '../utils/internal-store-utils';
+
+export interface ResourceWaitState {
+ resources: Set;
+}
+
+@Injectable({
+ providedIn: 'root',
+})
+export class ResourceWaitService {
+ private store = new InternalStore({ resources: new Set() });
+
+ getLoading() {
+ return !!this.store.state.resources.size;
+ }
+
+ getLoading$() {
+ return this.store.sliceState(({ resources }) => !!resources.size);
+ }
+
+ updateLoading$() {
+ return this.store.sliceUpdate(({ resources }) => !!resources.size);
+ }
+
+ clearLoading() {
+ this.store.patch({ resources: new Set() });
+ }
+
+ addResource(resource: string) {
+ const resources = this.store.state.resources;
+ resources.add(resource);
+ this.store.patch({ resources });
+ }
+
+ deleteResource(resource: string) {
+ const resources = this.store.state.resources;
+ resources.delete(resource);
+ this.store.patch({ resources });
+ }
+}
diff --git a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts
index 204118aa56..d880c94e5a 100644
--- a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts
+++ b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts
@@ -15,13 +15,13 @@ import { EnvironmentService } from './environment.service';
})
export class RestService {
constructor(
- @Inject(CORE_OPTIONS) private options: ABP.Root,
- private http: HttpClient,
- private store: Store,
- private environment: EnvironmentService,
+ @Inject(CORE_OPTIONS) protected options: ABP.Root,
+ protected http: HttpClient,
+ protected environment: EnvironmentService,
+ protected store: Store,
) {}
- private getApiFromStore(apiName: string): string {
+ protected getApiFromStore(apiName: string): string {
return this.environment.getApiUrl(apiName);
}
diff --git a/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts b/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts
new file mode 100644
index 0000000000..d1306e63c9
--- /dev/null
+++ b/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts
@@ -0,0 +1,53 @@
+import { Injectable } from '@angular/core';
+import {
+ NavigationCancel,
+ NavigationEnd,
+ NavigationError,
+ NavigationStart,
+ Router,
+} from '@angular/router';
+import { filter } from 'rxjs/operators';
+import { InternalStore } from '../utils/internal-store-utils';
+
+export interface RouterWaitState {
+ loading: boolean;
+}
+
+@Injectable({
+ providedIn: 'root',
+})
+export class RouterWaitService {
+ private store = new InternalStore({ loading: false });
+ constructor(private router: Router) {
+ this.router.events
+ .pipe(
+ filter(
+ event =>
+ event instanceof NavigationStart ||
+ event instanceof NavigationEnd ||
+ event instanceof NavigationError ||
+ event instanceof NavigationCancel,
+ ),
+ )
+ .subscribe(event => {
+ if (event instanceof NavigationStart) this.setLoading(true);
+ else this.setLoading(false);
+ });
+ }
+
+ getLoading() {
+ return this.store.state.loading;
+ }
+
+ getLoading$() {
+ return this.store.sliceState(({ loading }) => loading);
+ }
+
+ updateLoading$() {
+ return this.store.sliceUpdate(({ loading }) => loading);
+ }
+
+ setLoading(loading: boolean) {
+ this.store.patch({ loading });
+ }
+}
diff --git a/npm/ng-packs/packages/core/src/lib/tests/api.interceptor.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/api.interceptor.spec.ts
index ed42194755..ab289ddd9a 100644
--- a/npm/ng-packs/packages/core/src/lib/tests/api.interceptor.spec.ts
+++ b/npm/ng-packs/packages/core/src/lib/tests/api.interceptor.spec.ts
@@ -1,31 +1,29 @@
import { HttpRequest } from '@angular/common/http';
import { SpyObject } from '@ngneat/spectator';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
-import { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { Subject, timer } from 'rxjs';
-import { StartLoader, StopLoader } from '../actions';
import { ApiInterceptor } from '../interceptors';
-import { SessionStateService } from '../services';
+import { HttpWaitService, SessionStateService } from '../services';
describe('ApiInterceptor', () => {
let spectator: SpectatorService;
let interceptor: ApiInterceptor;
- let store: SpyObject;
let oauthService: SpyObject;
let sessionState: SpyObject;
+ let httpWaitService: SpyObject;
const createService = createServiceFactory({
service: ApiInterceptor,
- mocks: [OAuthService, Store, SessionStateService],
+ mocks: [OAuthService, SessionStateService],
});
beforeEach(() => {
spectator = createService();
interceptor = spectator.service;
- store = spectator.inject(Store);
sessionState = spectator.inject(SessionStateService);
oauthService = spectator.inject(OAuthService);
+ httpWaitService = spectator.inject(HttpWaitService);
});
it('should add headers to http request', done => {
@@ -52,8 +50,9 @@ describe('ApiInterceptor', () => {
handleRes$.complete();
});
- it('should dispatch the loader', done => {
- const spy = jest.spyOn(store, 'dispatch');
+ it('should call http wait services add request and delete request', done => {
+ const spyAddRequest = jest.spyOn(httpWaitService, 'addRequest');
+ const spyDeleteRequest = jest.spyOn(httpWaitService, 'deleteRequest');
const request = new HttpRequest('GET', 'https://abp.io');
const handleRes$ = new Subject();
@@ -70,8 +69,8 @@ describe('ApiInterceptor', () => {
handleRes$.complete();
timer(0).subscribe(() => {
- expect(spy.mock.calls[0][0] instanceof StartLoader).toBeTruthy();
- expect(spy.mock.calls[1][0] instanceof StopLoader).toBeTruthy();
+ expect(spyAddRequest).toHaveBeenCalled();
+ expect(spyDeleteRequest).toHaveBeenCalled();
done();
});
});
diff --git a/npm/ng-packs/packages/core/src/lib/tests/lazy-load.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/lazy-load.service.spec.ts
index dc4d937f17..eb347be80a 100644
--- a/npm/ng-packs/packages/core/src/lib/tests/lazy-load.service.spec.ts
+++ b/npm/ng-packs/packages/core/src/lib/tests/lazy-load.service.spec.ts
@@ -2,10 +2,12 @@ import { of, throwError } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { LazyLoadService } from '../services/lazy-load.service';
import { ScriptLoadingStrategy } from '../strategies';
+import { ResourceWaitService } from '../services';
describe('LazyLoadService', () => {
describe('#load', () => {
- const service = new LazyLoadService();
+ const resourceWaitService = new ResourceWaitService();
+ const service = new LazyLoadService(resourceWaitService);
const strategy = new ScriptLoadingStrategy('http://example.com/');
afterEach(() => {
@@ -58,7 +60,8 @@ describe('LazyLoadService', () => {
});
describe('#remove', () => {
- const service = new LazyLoadService();
+ const resourceWaitService = new ResourceWaitService();
+ const service = new LazyLoadService(resourceWaitService);
it('should remove an already lazy loaded element and return true', () => {
const script = document.createElement('script');
diff --git a/npm/ng-packs/packages/core/testing/src/lib/core-testing.module.ts b/npm/ng-packs/packages/core/testing/src/lib/core-testing.module.ts
index 5a43aa9a4c..7fa32a1b89 100644
--- a/npm/ng-packs/packages/core/testing/src/lib/core-testing.module.ts
+++ b/npm/ng-packs/packages/core/testing/src/lib/core-testing.module.ts
@@ -3,26 +3,27 @@ import {
BaseCoreModule,
coreOptionsFactory,
CORE_OPTIONS,
- LocalizationPipe,
+ LIST_QUERY_DEBOUNCE_TIME,
+ RestService,
} from '@abp/ng.core';
import { APP_BASE_HREF } from '@angular/common';
import { ModuleWithProviders, NgModule } from '@angular/core';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideRoutes } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
-import { MockLocalizationPipe } from './pipes/mock-localization.pipe';
+import { MockRestService } from './services/mock-rest.service';
/**
* CoreTestingModule is the module that will be used in tests
* and it provides mock alternatives
*/
@NgModule({
- exports: [RouterTestingModule, BaseCoreModule, MockLocalizationPipe],
- imports: [RouterTestingModule, BaseCoreModule],
- declarations: [MockLocalizationPipe],
+ exports: [RouterTestingModule, BaseCoreModule],
+ imports: [NoopAnimationsModule, RouterTestingModule, BaseCoreModule],
})
export class CoreTestingModule {
- static forTest(
- { baseHref = '/', routes = [], ...options } = {} as ABP.Test,
+ static withConfig(
+ { baseHref = '/', listQueryDebounceTime = 0, routes = [], ...options } = {} as ABP.Test,
): ModuleWithProviders {
return {
ngModule: CoreTestingModule,
@@ -38,8 +39,12 @@ export class CoreTestingModule {
deps: ['CORE_OPTIONS'],
},
{
- provide: LocalizationPipe,
- useClass: MockLocalizationPipe,
+ provide: LIST_QUERY_DEBOUNCE_TIME,
+ useValue: listQueryDebounceTime,
+ },
+ {
+ provide: RestService,
+ useClass: MockRestService,
},
provideRoutes(routes),
],
diff --git a/npm/ng-packs/packages/core/testing/src/lib/pipes/index.ts b/npm/ng-packs/packages/core/testing/src/lib/pipes/index.ts
deleted file mode 100644
index 60bb9b7dce..0000000000
--- a/npm/ng-packs/packages/core/testing/src/lib/pipes/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './mock-localization.pipe';
diff --git a/npm/ng-packs/packages/core/testing/src/lib/pipes/mock-localization.pipe.ts b/npm/ng-packs/packages/core/testing/src/lib/pipes/mock-localization.pipe.ts
deleted file mode 100644
index 302173cb0f..0000000000
--- a/npm/ng-packs/packages/core/testing/src/lib/pipes/mock-localization.pipe.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Config } from '@abp/ng.core';
-import { Injectable, Pipe, PipeTransform } from '@angular/core';
-
-@Injectable()
-@Pipe({
- name: 'abpLocalization',
-})
-export class MockLocalizationPipe implements PipeTransform {
- transform(value: string | Config.LocalizationWithDefault = '', ..._: string[]) {
- return typeof value === 'string' ? value : value.defaultValue;
- }
-}
diff --git a/npm/ng-packs/packages/core/testing/src/lib/services/index.ts b/npm/ng-packs/packages/core/testing/src/lib/services/index.ts
new file mode 100644
index 0000000000..24206d7c5d
--- /dev/null
+++ b/npm/ng-packs/packages/core/testing/src/lib/services/index.ts
@@ -0,0 +1 @@
+export * from './mock-rest.service';
diff --git a/npm/ng-packs/packages/core/testing/src/lib/services/mock-rest.service.ts b/npm/ng-packs/packages/core/testing/src/lib/services/mock-rest.service.ts
new file mode 100644
index 0000000000..b7b4066a4c
--- /dev/null
+++ b/npm/ng-packs/packages/core/testing/src/lib/services/mock-rest.service.ts
@@ -0,0 +1,21 @@
+import { ABP, CORE_OPTIONS, EnvironmentService, RestService } from '@abp/ng.core';
+import { HttpClient } from '@angular/common/http';
+import { Inject, Injectable } from '@angular/core';
+import { Observable, throwError } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class MockRestService extends RestService {
+ constructor(
+ @Inject(CORE_OPTIONS) protected options: ABP.Root,
+ protected http: HttpClient,
+ protected environment: EnvironmentService,
+ ) {
+ super(options, http, environment, null);
+ }
+
+ handleError(err: any): Observable {
+ return throwError(err);
+ }
+}
diff --git a/npm/ng-packs/packages/core/testing/src/public-api.ts b/npm/ng-packs/packages/core/testing/src/public-api.ts
index 8028a99aac..3b48853680 100644
--- a/npm/ng-packs/packages/core/testing/src/public-api.ts
+++ b/npm/ng-packs/packages/core/testing/src/public-api.ts
@@ -1,2 +1 @@
export * from './lib/core-testing.module';
-export * from './lib/pipes';
diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/ui-extensions.module.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/ui-extensions.module.ts
index 11f9ff9e4d..28456e0324 100644
--- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/ui-extensions.module.ts
+++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/ui-extensions.module.ts
@@ -47,4 +47,10 @@ import { PropDataDirective } from './directives/prop-data.directive';
NgbTypeaheadModule,
],
})
+export class BaseUiExtensionsModule {}
+
+@NgModule({
+ exports: [BaseUiExtensionsModule],
+ imports: [BaseUiExtensionsModule],
+})
export class UiExtensionsModule {}
diff --git a/npm/ng-packs/packages/theme-shared/extensions/testing/ng-package.json b/npm/ng-packs/packages/theme-shared/extensions/testing/ng-package.json
new file mode 100644
index 0000000000..10757e7f04
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/extensions/testing/ng-package.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
+ "dest": "../../dist/theme-shared/extensions/testing",
+ "lib": {
+ "entryFile": "src/public-api.ts"
+ }
+}
diff --git a/npm/ng-packs/packages/theme-shared/extensions/testing/src/lib/ui-extensions-testing.module.ts b/npm/ng-packs/packages/theme-shared/extensions/testing/src/lib/ui-extensions-testing.module.ts
new file mode 100644
index 0000000000..593cce3733
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/extensions/testing/src/lib/ui-extensions-testing.module.ts
@@ -0,0 +1,8 @@
+import { BaseUiExtensionsModule } from '@abp/ng.theme.shared/extensions';
+import { NgModule } from '@angular/core';
+
+@NgModule({
+ exports: [BaseUiExtensionsModule],
+ imports: [BaseUiExtensionsModule],
+})
+export class UiExtensionsTestingModule {}
diff --git a/npm/ng-packs/packages/theme-shared/extensions/testing/src/public-api.ts b/npm/ng-packs/packages/theme-shared/extensions/testing/src/public-api.ts
new file mode 100644
index 0000000000..d524c9cd0c
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/extensions/testing/src/public-api.ts
@@ -0,0 +1 @@
+export * from './lib/ui-extensions-testing.module';
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts
index f94b6b3656..1700efafe2 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts
@@ -1,9 +1,7 @@
-import { StartLoader, StopLoader, SubscriptionService } from '@abp/ng.core';
+import { HttpWaitService, RouterWaitService, SubscriptionService } from '@abp/ng.core';
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
-import { NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
-import { Actions, ofActionSuccessful } from '@ngxs/store';
-import { Subscription, timer } from 'rxjs';
-import { filter } from 'rxjs/operators';
+import { Router } from '@angular/router';
+import { combineLatest, Subscription, timer } from 'rxjs';
@Component({
selector: 'abp-loader-bar',
@@ -43,18 +41,14 @@ export class LoaderBarComponent implements OnDestroy, OnInit {
progressLevel = 0;
- interval: Subscription;
+ interval = new Subscription();
- timer: Subscription;
+ timer = new Subscription();
intervalPeriod = 350;
stopDelay = 800;
- @Input()
- filter = (action: StartLoader | StopLoader) =>
- action.payload.url.indexOf('openid-configuration') < 0;
-
private readonly clearProgress = () => {
this.progressLevel = 0;
this.cdRef.detectChanges();
@@ -78,63 +72,47 @@ export class LoaderBarComponent implements OnDestroy, OnInit {
}
constructor(
- private actions: Actions,
private router: Router,
private cdRef: ChangeDetectorRef,
private subscription: SubscriptionService,
+ private httpWaitService: HttpWaitService,
+ private routerWaiterService: RouterWaitService,
) {}
- private subscribeToLoadActions() {
- this.subscription.addOne(
- this.actions.pipe(ofActionSuccessful(StartLoader, StopLoader), filter(this.filter)),
- action => {
- if (action instanceof StartLoader) this.startLoading();
- else this.stopLoading();
- },
- );
+ ngOnInit() {
+ this.subscribeLoading();
}
- private subscribeToRouterEvents() {
+ subscribeLoading() {
this.subscription.addOne(
- this.router.events.pipe(
- filter(
- event =>
- event instanceof NavigationStart ||
- event instanceof NavigationEnd ||
- event instanceof NavigationError,
- ),
- ),
- event => {
- if (event instanceof NavigationStart) this.startLoading();
+ combineLatest([this.httpWaitService.getLoading$(), this.routerWaiterService.getLoading$()]),
+ ([httpLoading, routerLoading]) => {
+ if (httpLoading || routerLoading) this.startLoading();
else this.stopLoading();
},
);
}
- ngOnInit() {
- this.subscribeToLoadActions();
- this.subscribeToRouterEvents();
- }
-
ngOnDestroy() {
- if (this.interval) this.interval.unsubscribe();
+ this.interval.unsubscribe();
}
startLoading() {
- if (this.isLoading || (this.interval && !this.interval.closed)) return;
+ if (this.isLoading || !this.interval.closed) return;
this.isLoading = true;
-
+ this.progressLevel = 0;
this.interval = timer(0, this.intervalPeriod).subscribe(this.reportProgress);
+ this.timer.unsubscribe();
}
stopLoading() {
- if (this.interval) this.interval.unsubscribe();
+ this.interval.unsubscribe();
this.progressLevel = 100;
this.isLoading = false;
- if (this.timer && !this.timer.closed) return;
+ if (!this.timer.closed) return;
this.timer = timer(this.stopDelay).subscribe(this.clearProgress);
}
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts
index 46652f8b25..97f9533cf7 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts
@@ -74,7 +74,6 @@ export class ErrorHandler {
private actions: Actions,
private router: Router,
private confirmationService: ConfirmationService,
- private appRef: ApplicationRef,
private cfRes: ComponentFactoryResolver,
private rendererFactory: RendererFactory2,
private injector: Injector,
@@ -281,14 +280,16 @@ export class ErrorHandler {
}
this.componentRef.instance.hideCloseIcon = this.httpErrorConfig.errorScreen.hideCloseIcon;
+ const appRef = this.injector.get(ApplicationRef);
+
if (this.canCreateCustomError(instance.status as ErrorScreenErrorCodes)) {
this.componentRef.instance.cfRes = this.cfRes;
- this.componentRef.instance.appRef = this.appRef;
+ this.componentRef.instance.appRef = appRef;
this.componentRef.instance.injector = this.injector;
this.componentRef.instance.customComponent = this.httpErrorConfig.errorScreen.component;
}
- this.appRef.attachView(this.componentRef.hostView);
+ appRef.attachView(this.componentRef.hostView);
renderer.appendChild(host, (this.componentRef.hostView as EmbeddedViewRef).rootNodes[0]);
const destroy$ = new Subject();
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts
index 88813c44de..ab63cb3cb5 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts
@@ -13,7 +13,7 @@ import { ConfirmationService } from '../services';
exports: [ConfirmationComponent],
entryComponents: [ConfirmationComponent],
declarations: [ConfirmationComponent],
- imports: [CoreTestingModule.forTest()],
+ imports: [CoreTestingModule.withConfig()],
})
export class MockModule {}
@@ -22,7 +22,7 @@ describe('ConfirmationService', () => {
let service: ConfirmationService;
const createService = createServiceFactory({
service: ConfirmationService,
- imports: [NgxsModule.forRoot(), CoreTestingModule.forTest(), MockModule],
+ imports: [NgxsModule.forRoot(), CoreTestingModule.withConfig(), MockModule],
});
beforeEach(() => {
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts
index 939f87e5ee..a6908e03bf 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts
@@ -31,7 +31,7 @@ const CONFIRMATION_BUTTONS = {
describe('ErrorHandler', () => {
const createService = createServiceFactory({
service: ErrorHandler,
- imports: [NgxsModule.forRoot([]), CoreTestingModule.forTest(), MockModule],
+ imports: [NgxsModule.forRoot([]), CoreTestingModule.withConfig(), MockModule],
mocks: [OAuthService],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts
index 894b5f60d1..6d1ab85a46 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts
@@ -1,47 +1,37 @@
-import {
- Router,
- RouteReuseStrategy,
- NavigationStart,
- NavigationEnd,
- NavigationError,
-} from '@angular/router';
-import { createHostFactory, SpectatorHost, SpyObject } from '@ngneat/spectator/jest';
-import { Actions, NgxsModule, Store } from '@ngxs/store';
-import { Subject, Subscription, Observable, Subscriber, timer } from 'rxjs';
+import { NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
+import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator/jest';
+import { Subject, timer } from 'rxjs';
import { LoaderBarComponent } from '../components/loader-bar/loader-bar.component';
-import { StartLoader, StopLoader, SubscriptionService } from '@abp/ng.core';
+import { HttpWaitService, SubscriptionService } from '@abp/ng.core';
import { HttpRequest } from '@angular/common/http';
describe('LoaderBarComponent', () => {
- let spectator: SpectatorHost;
+ let spectator: Spectator;
let router: SpyObject;
const events$ = new Subject();
- const createHost = createHostFactory({
+ const createComponent = createComponentFactory({
component: LoaderBarComponent,
- mocks: [Router],
- imports: [NgxsModule.forRoot()],
detectChanges: false,
- providers: [SubscriptionService],
+ providers: [SubscriptionService, { provide: Router, useValue: { events: events$ } }],
});
beforeEach(() => {
- spectator = createHost('');
+ spectator = createComponent({});
spectator.component.intervalPeriod = 1;
spectator.component.stopDelay = 1;
router = spectator.inject(Router);
- (router as any).events = events$;
});
it('should initial variable values are correct', () => {
- spectator.component.interval = new Subscription();
expect(spectator.component.containerClass).toBe('abp-loader-bar');
expect(spectator.component.color).toBe('#77b6ff');
});
it('should increase the progressLevel', done => {
spectator.detectChanges();
- spectator.inject(Store).dispatch(new StartLoader(new HttpRequest('GET', 'test')));
+ const httpWaitService = spectator.inject(HttpWaitService);
+ httpWaitService.addRequest(new HttpRequest('GET', 'test'));
spectator.detectChanges();
setTimeout(() => {
expect(spectator.component.progressLevel > 0).toBeTruthy();
@@ -51,7 +41,8 @@ describe('LoaderBarComponent', () => {
test.skip('should be interval unsubscribed', done => {
spectator.detectChanges();
- spectator.inject(Store).dispatch(new StartLoader(new HttpRequest('GET', 'test')));
+ const httpWaitService = spectator.inject(HttpWaitService);
+ httpWaitService.addRequest(new HttpRequest('GET', 'test'));
expect(spectator.component.interval.closed).toBe(false);
timer(400).subscribe(() => {
@@ -62,11 +53,11 @@ describe('LoaderBarComponent', () => {
it('should start and stop the loading with navigation', done => {
spectator.detectChanges();
- (router as any).events.next(new NavigationStart(1, 'test'));
+ events$.next(new NavigationStart(1, 'test'));
expect(spectator.component.interval.closed).toBe(false);
- (router as any).events.next(new NavigationEnd(1, 'test', 'test'));
- (router as any).events.next(new NavigationError(1, 'test', 'test'));
+ events$.next(new NavigationEnd(1, 'test', 'test'));
+ events$.next(new NavigationError(1, 'test', 'test'));
expect(spectator.component.progressLevel).toBe(100);
timer(2).subscribe(() => {
@@ -77,10 +68,10 @@ describe('LoaderBarComponent', () => {
it('should stop the loading with navigation', done => {
spectator.detectChanges();
- (router as any).events.next(new NavigationStart(1, 'test'));
+ events$.next(new NavigationStart(1, 'test'));
expect(spectator.component.interval.closed).toBe(false);
- spectator.inject(Store).dispatch(new StopLoader(new HttpRequest('GET', 'test')));
+ events$.next(new NavigationEnd(1, 'testend', 'testend'));
expect(spectator.component.progressLevel).toBe(100);
timer(2).subscribe(() => {
@@ -92,8 +83,8 @@ describe('LoaderBarComponent', () => {
describe('#startLoading', () => {
it('should return when isLoading is true', done => {
spectator.detectChanges();
- (router as any).events.next(new NavigationStart(1, 'test'));
- (router as any).events.next(new NavigationStart(1, 'test'));
+ events$.next(new NavigationStart(1, 'test'));
+ events$.next(new NavigationStart(1, 'test'));
done();
});
});
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts
index fa6e0e578c..ab885726de 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts
@@ -11,7 +11,7 @@ import { ToasterService } from '../services/toaster.service';
exports: [ToastContainerComponent],
entryComponents: [ToastContainerComponent],
declarations: [ToastContainerComponent, ToastComponent],
- imports: [CoreTestingModule.forTest()],
+ imports: [CoreTestingModule.withConfig()],
})
export class MockModule {}
const toastClassPrefix = 'abp-toast';
@@ -21,7 +21,7 @@ describe('ToasterService', () => {
let service: ToasterService;
const createService = createServiceFactory({
service: ToasterService,
- imports: [NgxsModule.forRoot(), CoreTestingModule.forTest(), MockModule],
+ imports: [NgxsModule.forRoot(), CoreTestingModule.withConfig(), MockModule],
});
beforeEach(() => {
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
index c4647cbcd1..6775c15e5a 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
@@ -27,7 +27,7 @@ import { initLazyStyleHandler } from './handlers/lazy-style.handler';
import { RootParams } from './models/common';
import { THEME_SHARED_ROUTE_PROVIDERS } from './providers/route.provider';
import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token';
-import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.token';
+import { httpErrorConfigFactory, HTTP_ERROR_CONFIG } from './tokens/http-error.token';
import { DateParserFormatter } from './utils/date-parser-formatter';
const declarationsWithExports = [
@@ -48,17 +48,11 @@ const declarationsWithExports = [
LoadingDirective,
TableSortDirective,
];
+
@NgModule({
imports: [CoreModule, NgxDatatableModule, NgxValidateCoreModule, NgbPaginationModule],
- declarations: [
- ...declarationsWithExports,
- HttpErrorWrapperComponent,
- ModalContainerComponent,
- ],
- exports: [
- NgxDatatableModule,
- ...declarationsWithExports,
- ],
+ declarations: [...declarationsWithExports, HttpErrorWrapperComponent, ModalContainerComponent],
+ exports: [NgxDatatableModule, ...declarationsWithExports],
providers: [DatePipe],
entryComponents: [
HttpErrorWrapperComponent,
@@ -68,13 +62,23 @@ const declarationsWithExports = [
ConfirmationComponent,
],
})
-export class ThemeSharedModule {
- constructor(private errorHandler: ErrorHandler) {}
+export class BaseThemeSharedModule {}
+@NgModule({
+ imports: [BaseThemeSharedModule],
+ exports: [BaseThemeSharedModule],
+})
+export class ThemeSharedModule {
static forRoot(options = {} as RootParams): ModuleWithProviders {
return {
ngModule: ThemeSharedModule,
providers: [
+ {
+ provide: APP_INITIALIZER,
+ multi: true,
+ deps: [ErrorHandler],
+ useFactory: noop,
+ },
THEME_SHARED_ROUTE_PROVIDERS,
{
provide: APP_INITIALIZER,
diff --git a/npm/ng-packs/packages/theme-shared/testing/ng-package.json b/npm/ng-packs/packages/theme-shared/testing/ng-package.json
new file mode 100644
index 0000000000..7fc8d6067d
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/testing/ng-package.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "dest": "../../dist/theme-shared/testing",
+ "lib": {
+ "entryFile": "src/public-api.ts"
+ }
+}
diff --git a/npm/ng-packs/packages/theme-shared/testing/src/lib/theme-shared-testing.module.ts b/npm/ng-packs/packages/theme-shared/testing/src/lib/theme-shared-testing.module.ts
new file mode 100644
index 0000000000..323281046a
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/testing/src/lib/theme-shared-testing.module.ts
@@ -0,0 +1,27 @@
+import {
+ BaseThemeSharedModule,
+ DateParserFormatter,
+ THEME_SHARED_ROUTE_PROVIDERS,
+} from '@abp/ng.theme.shared';
+import { ModuleWithProviders, NgModule } from '@angular/core';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap';
+
+/**
+ * ThemeSharedTestingModule is the module that will be used in tests
+ */
+@NgModule({
+ exports: [RouterTestingModule, BaseThemeSharedModule],
+ imports: [RouterTestingModule, BaseThemeSharedModule],
+})
+export class ThemeSharedTestingModule {
+ static withConfig(): ModuleWithProviders {
+ return {
+ ngModule: ThemeSharedTestingModule,
+ providers: [
+ THEME_SHARED_ROUTE_PROVIDERS,
+ { provide: NgbDateParserFormatter, useClass: DateParserFormatter },
+ ],
+ };
+ }
+}
diff --git a/npm/ng-packs/packages/theme-shared/testing/src/public-api.ts b/npm/ng-packs/packages/theme-shared/testing/src/public-api.ts
new file mode 100644
index 0000000000..14355a8a28
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/testing/src/public-api.ts
@@ -0,0 +1 @@
+export * from './lib/theme-shared-testing.module';
diff --git a/npm/ng-packs/yarn.lock b/npm/ng-packs/yarn.lock
index 4ef0c07ef8..a3f300d1c8 100644
--- a/npm/ng-packs/yarn.lock
+++ b/npm/ng-packs/yarn.lock
@@ -2,12 +2,12 @@
# yarn lockfile v1
-"@abp/ng.core@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-4.1.0-rc.1.tgz#efbd35e8df0731e75b2195149269b0228ec4ae42"
- integrity sha512-WhXHMlDSbYled+mW8faMW8qHUGVErh/E/3eOgfvhx4N2tGGs5w+A4EfjNVBBROcoLDwiX2q1L88WeNnrK1+OIQ==
+"@abp/ng.core@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-4.1.0-rc.2.tgz#5fe53658965af66fae655dfce4bf6a7ad7105032"
+ integrity sha512-NcjOn1HYIdLTdHPbWTiLywPp1jQ+zmoeknnb0q/JfUUDn5r5kPrCf85xiZBUo8e+2ViB5vXvkM5lOOD5RfES6A==
dependencies:
- "@abp/utils" "^4.0.1"
+ "@abp/utils" "^4.1.0-rc.1"
"@angular/localize" "~10.0.10"
"@ngxs/store" "^3.7.0"
angular-oauth2-oidc "^10.0.0"
@@ -17,35 +17,35 @@
ts-toolbelt "6.15.4"
tslib "^2.0.0"
-"@abp/ng.feature-management@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.feature-management/-/ng.feature-management-4.1.0-rc.1.tgz#5237db2e5a0ea5a6511941864fa721a303b5612c"
- integrity sha512-/9GP6ze127uMkHB2QxWJC2MXiEMTLtBhM0DXYlPZKI+Y+FrVOBoUTI3gNz+AqWFwbBAA4PqfVuThhoja5b29Sg==
+"@abp/ng.feature-management@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.feature-management/-/ng.feature-management-4.1.0-rc.2.tgz#02e760bfa913276e694dbff545e39210a69b21bd"
+ integrity sha512-vf//thBD3ve8tkmAIan2FqQeHrO/sP8NVV83noGVMTpqKIAWIu7PBuSL3YqcNVOVxHLsO6EIwr63OAJytlvH6A==
dependencies:
- "@abp/ng.theme.shared" "~4.1.0-rc.1"
+ "@abp/ng.theme.shared" "~4.1.0-rc.2"
tslib "^2.0.0"
-"@abp/ng.identity@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.identity/-/ng.identity-4.1.0-rc.1.tgz#932fd10482e82400a87927d200c2d3c850cccef4"
- integrity sha512-uh7Fy+X2qGiNZ58g2c1GkmJL9VnMPLsKzg9EugNV2VxbGYg4/sNmVvAeIV66x2hpDXETHCjm+7lYuZsqI/5Vew==
+"@abp/ng.identity@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.identity/-/ng.identity-4.1.0-rc.2.tgz#381d956cebe2f3c272c59567ff424d3729adb6aa"
+ integrity sha512-4Wnx/iXAOgdexDYuNTV+DBdnjyfhcgca1ZiUSuEnSVDKePq0xvpGeKvpXzhhm9T3uXNsqYwqNq/X5jK3jd6mZg==
dependencies:
- "@abp/ng.permission-management" "~4.1.0-rc.1"
- "@abp/ng.theme.shared" "~4.1.0-rc.1"
+ "@abp/ng.permission-management" "~4.1.0-rc.2"
+ "@abp/ng.theme.shared" "~4.1.0-rc.2"
tslib "^2.0.0"
-"@abp/ng.permission-management@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.permission-management/-/ng.permission-management-4.1.0-rc.1.tgz#de810f1d50f901cffb15f59756127e83109e79f7"
- integrity sha512-AgPpmBKDYnMKmMXoiU9ewGKtn5hH9frO5tV0wjz46BjmO9kIwyap3BBeCFcZjhgVLxZnXi8D0WuzZW7mgQARIQ==
+"@abp/ng.permission-management@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.permission-management/-/ng.permission-management-4.1.0-rc.2.tgz#b74402241886e156a0e72baddfbc0ebebb7576aa"
+ integrity sha512-6/Z/8KGKkAflfk6CoXk92StEELuP0xkwj2dvvFvnGpr2od9ELGWWqtiJ5Pjz6i5FQvrLdqcHqPh8x/vgr7QV9w==
dependencies:
- "@abp/ng.theme.shared" "~4.1.0-rc.1"
+ "@abp/ng.theme.shared" "~4.1.0-rc.2"
tslib "^2.0.0"
-"@abp/ng.schematics@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.schematics/-/ng.schematics-4.1.0-rc.1.tgz#42173b847cfc2f293245a96fe4732a9049b8ab7c"
- integrity sha512-FU5Mat3ki6ARDmOK5WC4/AMXJC+61EknuH44SE9Y9PraXAYkJk+2Ha+1qAg+VLBQcEEVWLxIqMp+2Soc6d6oow==
+"@abp/ng.schematics@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.schematics/-/ng.schematics-4.1.0-rc.2.tgz#31dada6fc1f69d0198f4fb3801b6009d6a0a0c09"
+ integrity sha512-yVi/C1IbwcOQfXUAe8FSlekdNfoqi/UQvTBQ19h/HGBBDxhqbv6QKjhkaVs/+EOAW8WHWCufyVtQ2/y/jrCeog==
dependencies:
"@angular-devkit/core" "~11.0.2"
"@angular-devkit/schematics" "~11.0.2"
@@ -53,37 +53,37 @@
jsonc-parser "^2.3.0"
typescript "~3.9.2"
-"@abp/ng.setting-management@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.setting-management/-/ng.setting-management-4.1.0-rc.1.tgz#d35083c211089683c4fb07f88523c01e3e5d8ce0"
- integrity sha512-qKLV2IWkHbK5ohHMxbdzPqE1pvPPatmV0EidE79Rk4j5+WZXOXtNz5GEA2vLmF3CsuVjX90cJQdtY1bdZpvdVQ==
+"@abp/ng.setting-management@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.setting-management/-/ng.setting-management-4.1.0-rc.2.tgz#4b61bff63fa9d68be5f1c7b8c47049cc1bd8b5a8"
+ integrity sha512-Df2QS0bsytytUSkQ0KrI/m4pVTpQS708fwl8t3KcpeQ4hlgdg6LtTmhUaY/j6k8M1RJHaKvjPE9Jg6R2daLNGg==
dependencies:
- "@abp/ng.theme.shared" "~4.1.0-rc.1"
+ "@abp/ng.theme.shared" "~4.1.0-rc.2"
tslib "^2.0.0"
-"@abp/ng.tenant-management@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management/-/ng.tenant-management-4.1.0-rc.1.tgz#be6740c8a7ac844c7100a46fb88bc96ce386e17d"
- integrity sha512-OymGkg5BzHiraUC0FcWPm6JN84FgYNZ3YuLTAZd/iHmqQVoj51ymTK8dBOn7sw49vWlAj/WvGN4Z9tT/JS40zg==
+"@abp/ng.tenant-management@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management/-/ng.tenant-management-4.1.0-rc.2.tgz#b75b60db124e5246476821da2bfeebd5e5da09b6"
+ integrity sha512-AnDiMa/M4xSwiSXjyIsBY13BNxm+JMhWiIEb9WAPlk8IUaNDJZH9zHJH/HQSUHdy6dFCmutl5Ezi6U1J9ZHpPQ==
dependencies:
- "@abp/ng.feature-management" "~4.1.0-rc.1"
- "@abp/ng.theme.shared" "~4.1.0-rc.1"
+ "@abp/ng.feature-management" "~4.1.0-rc.2"
+ "@abp/ng.theme.shared" "~4.1.0-rc.2"
tslib "^2.0.0"
-"@abp/ng.theme.basic@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.theme.basic/-/ng.theme.basic-4.1.0-rc.1.tgz#311e1ee4490803d43b1b1286620f1efa0fe3f459"
- integrity sha512-nTf9UaupTxT0UgBx/SwaVuhtutUXFqGGq2dBKI0JNxltgfykLhDXUOTlUnymLlsyu09DJv8OIZbCX6PJAMV1GA==
+"@abp/ng.theme.basic@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.theme.basic/-/ng.theme.basic-4.1.0-rc.2.tgz#4c0ccb5386a09cf7d89efaee59c8920265032361"
+ integrity sha512-2NjHymzE/sgbZ8e+CJ4iMemB+mD6e/VSKA2EkCaekPpXDlp0Z3f2yyhm0jwYqCQPsmCjykdtTpR266yLQOs7ug==
dependencies:
- "@abp/ng.theme.shared" "~4.1.0-rc.1"
+ "@abp/ng.theme.shared" "~4.1.0-rc.2"
tslib "^2.0.0"
-"@abp/ng.theme.shared@~4.1.0-rc.1":
- version "4.1.0-rc.1"
- resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-4.1.0-rc.1.tgz#08c051cafd1364563943f7f8b6d067955227cc7e"
- integrity sha512-oNPGXI6gyesDnzHq/x7QuXKr0ISwgz9YaKg2bvzZOCvyZ0usL2P4h/4sQvV9ThCYjDdkVKN+cdW+KNvalJ415w==
+"@abp/ng.theme.shared@~4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-4.1.0-rc.2.tgz#eaf524ef26f5433ec7ea01d402ba071c42ce1aa6"
+ integrity sha512-d6dVhV2wer7eeHoT8Y91Bqf7m2r3FVexUp7R+/f93564swuceZGZWSsgntwpHkNdDJaxhLUJ8tG5XMe2deEGTQ==
dependencies:
- "@abp/ng.core" "~4.1.0-rc.1"
+ "@abp/ng.core" "~4.1.0-rc.2"
"@fortawesome/fontawesome-free" "^5.14.0"
"@ng-bootstrap/ng-bootstrap" "^7.0.0"
"@ngx-validate/core" "^0.0.13"
@@ -92,13 +92,6 @@
chart.js "^2.9.3"
tslib "^2.0.0"
-"@abp/utils@^4.0.1":
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.0.1.tgz#9a4cb420266c49d8084776bd31c86681d4d4d125"
- integrity sha512-nI2ZyaNpvohr75p8jeMPWze+hX+mV47EHI+Zloa4GgGmJqrULdG71kYh45R+jGmMTbQL6huaXGnpz4gNF9TGyQ==
- dependencies:
- just-compare "^1.3.0"
-
"@abp/utils@^4.1.0-rc.1":
version "4.1.0-rc.1"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.1.0-rc.1.tgz#2863867985db9d178a5ce4347309f060f1ce6142"
@@ -106,6 +99,13 @@
dependencies:
just-compare "^1.3.0"
+"@abp/utils@^4.1.0-rc.2":
+ version "4.1.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.1.0-rc.2.tgz#eb6dbf0ee493d0f050b37347d2d6d283098aedae"
+ integrity sha512-V2k5I89lVBoeGIKgg4p2H9GlMAcDWbctwKZPVwBEMVEVm1uTR2xQAWdTdSFd5Q8I8Xsf/aIG8ELM7l5j2h7/zQ==
+ dependencies:
+ just-compare "^1.3.0"
+
"@angular-builders/jest@^10.0.0":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@angular-builders/jest/-/jest-10.0.1.tgz#a1a6fb5d11b5d54c051bdaa2012b5f046371560c"
diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Menus/MyProjectNameMenuContributor.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Menus/MyProjectNameMenuContributor.cs
index 8b5aebacce..c42223526a 100644
--- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Menus/MyProjectNameMenuContributor.cs
+++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Menus/MyProjectNameMenuContributor.cs
@@ -59,11 +59,10 @@ namespace MyCompanyName.MyProjectName.Blazor.Menus
context.Menu.AddItem(new ApplicationMenuItem(
"Account.Manage",
accountStringLocalizer["ManageYourProfile"],
- $"{identityServerUrl.EnsureEndsWith('/')}Account/Manage",
+ $"{identityServerUrl.EnsureEndsWith('/')}Account/Manage?returnUrl={_configuration["App:SelfUrl"]}",
icon: "fa fa-cog",
order: 1000,
- null,
- "_blank"));
+ null));
}
return Task.CompletedTask;
diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/wwwroot/appsettings.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/wwwroot/appsettings.json
index 997cf89e64..4349de1480 100644
--- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/wwwroot/appsettings.json
+++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/wwwroot/appsettings.json
@@ -1,4 +1,7 @@
{
+ "App": {
+ "SelfUrl": "https://localhost:44307"
+ },
"AuthServer": {
"Authority": "https://localhost:44305",
"ClientId": "MyProjectName_Blazor",
diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json
index 2e4a69bd49..1d9beacd54 100644
--- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json
+++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json
@@ -21,7 +21,7 @@
"MyProjectName_Swagger": {
"ClientId": "MyProjectName_Swagger",
"ClientSecret": "1q2w3e*",
- "RootUrl": "https://localhost:44300"
+ "RootUrl": "https://localhost:44305"
}
}
}
diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs
index 39cedbb06a..ac55c87569 100644
--- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs
+++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs
@@ -58,7 +58,7 @@ namespace MyCompanyName.MyProjectName
ConfigureLocalization();
ConfigureVirtualFileSystem(context);
ConfigureCors(context, configuration);
- ConfigureSwaggerServices(context);
+ ConfigureSwaggerServices(context, configuration);
}
private void ConfigureBundles()
@@ -129,9 +129,14 @@ namespace MyCompanyName.MyProjectName
});
}
- private static void ConfigureSwaggerServices(ServiceConfigurationContext context)
+ private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
{
- context.Services.AddSwaggerGen(
+ context.Services.AddAbpSwaggerGenWithOAuth(
+ configuration["AuthServer:Authority"],
+ new Dictionary
+ {
+ {"MyProjectName", "MyProjectName API"}
+ },
options =>
{
options.SwaggerDoc("v1", new OpenApiInfo {Title = "MyProjectName API", Version = "v1"});
@@ -216,6 +221,10 @@ namespace MyCompanyName.MyProjectName
app.UseAbpSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyProjectName API");
+
+ var configuration = context.ServiceProvider.GetRequiredService();
+ c.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
+ c.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
});
app.UseAuditing();
diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/appsettings.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/appsettings.json
index 795753ec9d..1051a8576c 100644
--- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/appsettings.json
+++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/appsettings.json
@@ -9,7 +9,9 @@
},
"AuthServer": {
"Authority": "https://localhost:44305",
- "RequireHttpsMetadata": "false"
+ "RequireHttpsMetadata": "false",
+ "SwaggerClientId": "MyProjectName_Swagger",
+ "SwaggerClientSecret": "1q2w3e*"
},
"StringEncryption": {
"DefaultPassPhrase": "gsKnGZ041HLL4IM8"
diff --git a/test/AbpPerfTest/AbpPerfTest.WithAbp/AbpPerfTest.WithAbp.csproj b/test/AbpPerfTest/AbpPerfTest.WithAbp/AbpPerfTest.WithAbp.csproj
index b270d019a4..3c738143b7 100644
--- a/test/AbpPerfTest/AbpPerfTest.WithAbp/AbpPerfTest.WithAbp.csproj
+++ b/test/AbpPerfTest/AbpPerfTest.WithAbp/AbpPerfTest.WithAbp.csproj
@@ -5,9 +5,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive