Browse Source

Merge branch 'dev' of https://github.com/abpframework/abp into dev

pull/1841/head
Halil İbrahim Kalkan 7 years ago
parent
commit
0b1c6b1fa6
  1. 35
      docs/cs/Best-Practices/PostgreSQL-Integration.md
  2. BIN
      docs/cs/Best-Practices/images/postgresql-delete-initial-migrations.png
  3. BIN
      docs/cs/Best-Practices/images/postgresql-update-database.png
  4. 61
      docs/cs/Dapper.md
  5. 126
      docs/cs/Getting-Started-Angular-Template.md
  6. 14
      docs/cs/docs-nav.json
  7. 4
      docs/en/Getting-Started-Angular-Template.md
  8. 31
      docs/es/Index.md
  9. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  10. 18
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs
  11. 3
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs
  12. 30
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs
  13. 27
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs
  14. 21
      npm/ng-packs/.vscode/settings.json
  15. 254
      npm/ng-packs/angular.json
  16. 12
      npm/ng-packs/apps/dev-app/browserslist
  17. 30
      npm/ng-packs/apps/dev-app/e2e/protractor.conf.js
  18. 28
      npm/ng-packs/apps/dev-app/e2e/src/app.e2e-spec.ts
  19. 11
      npm/ng-packs/apps/dev-app/e2e/src/app.po.ts
  20. 9
      npm/ng-packs/apps/dev-app/e2e/tsconfig.json
  21. 32
      npm/ng-packs/apps/dev-app/karma.conf.js
  22. 34
      npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts
  23. 10
      npm/ng-packs/apps/dev-app/src/app/app.component.ts
  24. 43
      npm/ng-packs/apps/dev-app/src/app/app.module.ts
  25. 18
      npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts
  26. 15
      npm/ng-packs/apps/dev-app/src/app/home/home.component.html
  27. 14
      npm/ng-packs/apps/dev-app/src/app/home/home.component.ts
  28. 10
      npm/ng-packs/apps/dev-app/src/app/home/home.module.ts
  29. 7
      npm/ng-packs/apps/dev-app/src/app/lazy-libs/account-wrapper.module.ts
  30. 7
      npm/ng-packs/apps/dev-app/src/app/lazy-libs/identity-wrapper.module.ts
  31. 7
      npm/ng-packs/apps/dev-app/src/app/lazy-libs/tenant-management-wrapper.module.ts
  32. 14
      npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts
  33. 0
      npm/ng-packs/apps/dev-app/src/assets/.gitkeep
  34. 25
      npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts
  35. 25
      npm/ng-packs/apps/dev-app/src/environments/environment.ts
  36. BIN
      npm/ng-packs/apps/dev-app/src/favicon.ico
  37. 13
      npm/ng-packs/apps/dev-app/src/index.html
  38. 13
      npm/ng-packs/apps/dev-app/src/main.ts
  39. 62
      npm/ng-packs/apps/dev-app/src/polyfills.ts
  40. 1
      npm/ng-packs/apps/dev-app/src/styles.scss
  41. 14
      npm/ng-packs/apps/dev-app/src/test.ts
  42. 38
      npm/ng-packs/apps/dev-app/tsconfig.app.json
  43. 9
      npm/ng-packs/apps/dev-app/tsconfig.spec.json
  44. 7
      npm/ng-packs/apps/dev-app/tslint.json
  45. 87
      npm/ng-packs/package.json
  46. 1
      npm/ng-packs/packages/account-config/README.md
  47. 32
      npm/ng-packs/packages/account-config/karma.conf.js
  48. 7
      npm/ng-packs/packages/account-config/ng-package.json
  49. 7
      npm/ng-packs/packages/account-config/package.json
  50. 37
      npm/ng-packs/packages/account-config/src/lib/account-config.module.ts
  51. 21
      npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts
  52. 2
      npm/ng-packs/packages/account-config/src/public-api.ts
  53. 21
      npm/ng-packs/packages/account-config/src/test.ts
  54. 26
      npm/ng-packs/packages/account-config/tsconfig.lib.json
  55. 17
      npm/ng-packs/packages/account-config/tsconfig.spec.json
  56. 17
      npm/ng-packs/packages/account-config/tslint.json
  57. 4
      npm/ng-packs/packages/account/src/lib/account.module.ts
  58. 33
      npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts
  59. 4
      npm/ng-packs/packages/account/src/lib/constants/routes.ts
  60. 8
      npm/ng-packs/packages/account/src/lib/services/account.service.ts
  61. 8
      npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts
  62. 8
      npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts
  63. 5
      npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts
  64. 9
      npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts
  65. 11
      npm/ng-packs/packages/core/src/lib/directives/for.directive.ts
  66. 16
      npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts
  67. 5
      npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts
  68. 10
      npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts
  69. 3
      npm/ng-packs/packages/core/src/lib/enums/common.ts
  70. 4
      npm/ng-packs/packages/core/src/lib/models/common.ts
  71. 5
      npm/ng-packs/packages/core/src/lib/models/config.ts
  72. 2
      npm/ng-packs/packages/core/src/lib/pipes/index.ts
  73. 45
      npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts
  74. 17
      npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts
  75. 27
      npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts
  76. 3
      npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts
  77. 19
      npm/ng-packs/packages/core/src/lib/services/localization.service.ts
  78. 6
      npm/ng-packs/packages/core/src/lib/services/rest.service.ts
  79. 80
      npm/ng-packs/packages/core/src/lib/states/config.state.ts
  80. 5
      npm/ng-packs/packages/core/src/lib/states/session.state.ts
  81. 343
      npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts
  82. 157
      npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts
  83. 37
      npm/ng-packs/packages/core/src/lib/tests/locale.provider.spec.ts
  84. 81
      npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts
  85. 15
      npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts
  86. 45
      npm/ng-packs/packages/core/src/lib/tests/state-overwrite.ts
  87. 1
      npm/ng-packs/packages/core/src/lib/utils/common-utils.ts
  88. 3
      npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts
  89. 7
      npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts
  90. 5
      npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts
  91. 18
      npm/ng-packs/packages/core/tsconfig.spec.json
  92. 20
      npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts
  93. 2
      npm/ng-packs/packages/feature-management/src/lib/states/index.ts
  94. 1
      npm/ng-packs/packages/identity-config/README.md
  95. 32
      npm/ng-packs/packages/identity-config/karma.conf.js
  96. 7
      npm/ng-packs/packages/identity-config/ng-package.json
  97. 7
      npm/ng-packs/packages/identity-config/package.json
  98. 8
      npm/ng-packs/packages/identity-config/src/lib/identity-config.module.ts
  99. 32
      npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts
  100. 2
      npm/ng-packs/packages/identity-config/src/public-api.ts

35
docs/cs/Best-Practices/PostgreSQL-Integration.md

@ -0,0 +1,35 @@
## Entity Framework Core PostgreSQL integrace
> Podívejte se na [Entity Framework Core integrační dokument](../Entity-Framework-Core.md) pro základy integrace EF Core.
### Aktualizace projektu EntityFrameworkCore
- V projektu `Acme.BookStore.EntityFrameworkCore` nahraďte balík `Volo.Abp.EntityFrameworkCore.SqlServer` za `Volo.Abp.EntityFrameworkCore.PostgreSql`
- Aktualizace pro použití PostgreSQL v `BookStoreEntityFrameworkCoreModule`
- Nahraďte `AbpEntityFrameworkCoreSqlServerModule` za `AbpEntityFrameworkCorePostgreSqlModule`
- Nahraďte `options.UseSqlServer()` za `options.UsePostgreSql()`
- V jiných projektech aktualizujte PostgreSQL connection string v nezbytných `appsettings.json` souborech
#### Odstranění stávajících migrací
Smažte všechny stavající migrační soubory (včetně `DbContextModelSnapshot`)
![postgresql-delete-initial-migrations](images/postgresql-delete-initial-migrations.png)
#### Znovu vygenerujte počáteční migraci & aktualizujte databázi
Nastavte správný spouštěcí projekt (obvykle web projekt),
Otevřete **Package Manager Console** (Tools -> Nuget Package Manager -> Package Manager Console), zvolte `Acme.BookStore.EntityFrameworkCore.DbMigrations` jako **Default project** a proveďte následující příkaz:
Proveďte příkaz `Add-Migration`:
````
PM> Add-Migration Initial
````
Poté proveďte příkaz `Update-Database` k aktualizaci schématu databáze:
````
PM> Update-Database
````
![postgresql-update-database](images/postgresql-update-database.png)

BIN
docs/cs/Best-Practices/images/postgresql-delete-initial-migrations.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/cs/Best-Practices/images/postgresql-update-database.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

61
docs/cs/Dapper.md

@ -0,0 +1,61 @@
# Dapper integrace
Jelikož myšlenka Dapper je taková, že sql příkaz má přednost, tak hlavně poskytuje metody rozšíření pro `IDbConnection` rozhraní.
Abp nezapouzdřuje přílíš mnoho funkcí pro Dapper. Abp Dapper poskytuje základní třídu `DapperRepository<TDbContext>` založenou na Abp EntityFrameworkCore, který poskytuje vlastnosti `IDbConnection` a `IDbTransaction` vyžadované v Dapper.
Tyto dvě vlastnosti fungují dobře s [jednotkou práce](Unit-Of-Work.md).
## Instalace
Nainstalujte a nakonfigurujte EF Core podle [EF Core integrační dokumentace](Entity-Framework-Core.md).
`Volo.Abp.Dapper` je hlavní NuGet balík pro Dapper integraci. Nainstalujte jej proto do vašeho projektu (pro strukturovanou aplikaci do datové/infrastrukturní vrstvy):
```shell
Install-Package Volo.Abp.Dapper
```
Poté přidejte závislost na `AbpDapperModule` modulu (atribut `DependsOn`) do Vašeho [modulu](Module-Development-Basics.md):
````C#
using Volo.Abp.Dapper;
using Volo.Abp.Modularity;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpDapperModule))]
public class MyModule : AbpModule
{
//...
}
}
````
## Implementace Dapper repozitáře
Následující kód implementuje repozitář `Person`, který vyžaduje `DbContext` z EF Core (MyAppDbContext). Můžete vložit `PersonDapperRepository` k volání jeho metod.
`DbConnection` a `DbTransaction` jsou ze základní třídy `DapperRepository`.
```C#
public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency
{
public PersonDapperRepository(IDbContextProvider<MyAppDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<string>> GetAllPersonNames()
{
return (await DbConnection.QueryAsync<string>("select Name from People", transaction: DbTransaction))
.ToList();
}
public virtual async Task<int> UpdatePersonNames(string name)
{
return await DbConnection.ExecuteAsync("update People set Name = @NewName", new { NewName = name },
DbTransaction);
}
}
```

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

@ -0,0 +1,126 @@
## Začínáme s Angular aplikační šablonou
Tento tutoriál vysvětluje, jak vytvořit novou Angular aplikaci pomocí spouštěcí šablony, jak ji nakonfigurovat a spustit.
### Tvorba nového projektu
Tento tutorial používá k vytvoření nového projektu **ABP CLI**. Podívejte se na stránku [začínáme](https://abp.io/get-started) pro více možností.
Pokud jste tak dosud neučinili, nainstalujte ABP CLI pomocí okna příkazového řádku:
````bash
dotnet tool install -g Volo.Abp.Cli
````
Použíjte příkaz `abp new` v prázdné složce k vytvoření Vašeho projektu:
````bash
abp new Acme.BookStore -u angular
````
> Můžete použít různé úrovně jmenných prostorů; např. BookStore, Acme.BookStore nebo Acme.Retail.BookStore.
`-u angular` volba specifikuje Angular jako UI framework. Výchozí poskytovatel databáze je EF Core. Podívejte se na [CLI dokumentaci](CLI.md) pro všechny dostupné možnosti.
#### Předběžné požadavky
Vytvořené řešení vyžaduje;
* [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/)
* [.NET Core 2.2+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v10.16+](https://nodejs.org)
* [Yarn v1.17+](https://yarnpkg.com/)
### Struktura řešení
Otevřete řešení ve **Visual Studio**:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-for-spa.png)
Řešení má vrstvenou strukturu (založenou na [domain driven designu](Domain-Driven-Design.md)) a obsahuje projekty testů jednotek a integrace správně nakonfigurované pro práci s **EF Core** & **SQLite in-memory** databází.
> Podívejte se na [dokument šablony aplikace](Startup-Templates/Application.md) k detailnímu pochopení struktury řešení.
### Databázový connection string
Zkontrolujte **connection string** v souboru `appsettings.json` u projektu `.HttpApi.Host`:
````json
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
}
}
````
Řešení je nakonfigurováno pro použití **Entity Framework Core** s **MS SQL Server**. EF Core podporuje [různé](https://docs.microsoft.com/en-us/ef/core/providers/) poskytovatele databáze, takže pokud chcete můžete použít jiný DBMS. V případě potřeby změňte connection string.
### Tvorba databáze & aplikace migrací databáze
K vytvoření databáze máte dvě možnosti.
#### Použití aplikace DbMigrator
Řešení obsahuje konzolovou aplikaci (v tomto příkladu nazvanou `Acme.BookStore.DbMigrator`), která dokáže vytvořit databázi, aplikovat migrace a vložit počáteční data. Ta je užitečná jak pro vývojové tak pro produkční prostředí.
> `.DbMigrator` má vlastní `appsettings.json`. Pokud jste změnili connection string výše, měli byste změnit i tento.
Klikněte pravým na projekt `.DbMigrator` zvolte **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
Zmáčkněte F5 (nebo Ctrl+F5) ke spuštění aplikace. Výstup by měl být podobný vyobrazení níže:
![set-as-startup-project](images/db-migrator-app.png)
#### Použití příkazu EF Core Update-Database
Ef Core máš příkaz `Update-Database`, který v případě potřeby vytvoří databázi a aplikuje čekající migrace. Klikněte pravým na projekt `.Web` a zvolte **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
Otevřete **Package Manager Console**, zvolte `.EntityFrameworkCore.DbMigrations` jako **Default Project** a proveďte příkaz `Update-Database`:
![pcm-update-database](images/pcm-update-database-v2.png)
Tímto vytvoříte novou databáze podle nakonfigurovaného connection string.
> Je doporučeno užití nástroje `.DbMigrator`, protože zároveň vloží i počáteční data ke správnému běhu webové aplikace.
### Spuštění aplikace
#### Spuštění API Host (na straně serveru)
Ujistěte se že je projekt `.HttpApi.Host` nastaven jako startovací a spusťte aplikaci což otevře Swagger UI:
![bookstore-homepage](images/bookstore-swagger-ui-host.png)
Tady můžete vidět API aplikace a zároveň je i otestovat. Získejte [více informací](https://swagger.io/tools/swagger-ui/) o Swagger UI.
##### Autorizace pro Swagger UI
Vetšina API aplikace vyžaduje autentizaci & autorizaci. Pokud chcete otestovat autorizované API, manuálně přejděte na stránku `/Account/Login`, vložte `admin` jako uživatelské jméno a `1q2w3E*` jako heslo k příhlášení do aplikace. Poté budete moci provádět autorizované požadavky API.
#### Spuštění Angular aplikace (na straně klienta)
Přejděte do složky `angular`, otevřete terminál příkazového řádku, proveďte příkaz `yarn` (doporučujeme používat správce balíků [yarn](https://yarnpkg.com), npm install bude v mnoha případech také fungovat):
````bash
yarn
````
Jakmile jsou načteny všechny node moduly, proveďte příkaz `yarn start` nebo `npm start`:
````bash
yarn start
````
Otevřete Váš oblíbený prohlížeč a přejděte na adresu `localhost:4200`. Počáteční uživatelské jméno je `admin` a heslo `1q2w3E*`.
Startovací šablona obsahuje moduly **správa identit** a **správa tenantů**. Jakmile se přihlásíte, zprístupní se administrační menu kde můžete spravovat **tenanty**, **role**, **uživatele** a jejich **oprávnění**.
> Doporučujeme [Visual Studio Code](https://code.visualstudio.com/) jako editor pro Angular projekt, ale klidně použijte Váš oblíbený editor.
### Co dále?
* [Tutoriál vývoje aplikace](Tutorials/Angular/Part-I.md)

14
docs/cs/docs-nav.json

@ -202,7 +202,7 @@
"items": [
{
"text": "API",
"items": [
"items": [
{
"text": "Automatické API řadiče",
"path": "AspNetCore/Auto-API-Controllers.md"
@ -245,11 +245,21 @@
"items": [
{
"text": "Entity Framework Core integrace",
"path": "Entity-Framework-Core.md"
"path": "Entity-Framework-Core.md",
"items": [
{
"text": "PostgreSQL integrace",
"path": "Best-Practices/PostgreSQL-Integration.md"
}
]
},
{
"text": "MongoDB integrace",
"path": "MongoDB.md"
},
{
"text": "Dapper integrace",
"path": "Dapper.md"
}
]
},

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

@ -85,13 +85,13 @@ Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations`
This will create a new database based on the configured connection string.
> Using the `.Migrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application.
> Using the `.DbMigrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application.
### Running the Application
#### Run the API Host (Server Side)
Ensure that the `.HttpApi.Host` project is the startup project and un the application which will open a Swagger UI:
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
![bookstore-homepage](images/bookstore-swagger-ui-host.png)

31
docs/es/Index.md

@ -0,0 +1,31 @@
# Documentación de ABP
ABP es un **marco de desarrollo de código abierto** enfocado en el desarrollo de aplicaciones web basadas en ASP.NET Core, pero tambien soporta el desarrollo de otro tipo de aplicaciones.
Explora el menú de navegación de la izquierda para profundizar en la documentación.
## Estado del proyecto
ABP es la **próxima generación** del marco de desarrollo de código abierto [ASP.NET Boilerplate](https://aspnetboilerplate.com/). Actualmente se encuentra en una fase preliminar y no está listo para su uso en producción. La documentación todavía está en progreso y se encuentra lejos de estar completa.
Para aplicaciones en fase de producción o que lo vayan a estar en el corto plazo, se sugiere usar el marco de desarrollo [ASP.NET Boilerplate](https://aspnetboilerplate.com/) el cual tiene un completo conjunto de funciones, es maduro, mantenido y actualizado activamente.
## Primeros pasos
La manera más facil para iniciar un proyecto con ABP es usar las plantillas de inicio:
* [Plantilla ASP.NET Core MVC (Razor Pages) UI](Getting-Started-AspNetCore-MVC-Template.md)
* [Plantilla Angular UI](Getting-Started-Angular-Template.md)
Si quieres iniciar desde cero (con un proyecto vacío), entonces instala manualmente el marco de desarrollo ABP y usa los siguientes tutoriales:
* [Aplicación de Consola](Getting-Started-Console-Application.md)
* [Aplicación web con ASP.NET Core](Getting-Started-AspNetCore-Application.md)
## Código fuente
ABP está hospedado en GitHub. Mira el [código fuente](https://github.com/abpframework/abp).
## ¿Quieres contribuir?
ABP es un proyecto de código abierto impulsado por la comunidad. Mira la [guía de contribución](Contribution/Index.md) si quieres formar parte de este proyecto.

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -227,17 +227,17 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
if (string.IsNullOrEmpty(TagHelper.Label))
{
return await GetLabelAsHtmlUsingTagHelperAsync(context, output, isCheckbox) + GetRequiredSymbol(context, output, inputTag);
return await GetLabelAsHtmlUsingTagHelperAsync(context, output, isCheckbox) + GetRequiredSymbol(context, output);
}
var checkboxClass = isCheckbox ? "class=\"custom-control-label\" " : "";
return "<label " + checkboxClass + GetIdAttributeAsString(inputTag) + ">"
+ TagHelper.Label +
"</label>" + GetRequiredSymbol(context, output, inputTag);
"</label>" + GetRequiredSymbol(context, output);
}
protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag)
protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output)
{
if (!TagHelper.DisplayRequiredSymbol)
{

18
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs

@ -169,8 +169,16 @@ namespace Volo.Abp.Cli
}
}
private static bool IsGlobalTool(string toolPath)
{
var globalPaths = new[] { @"%USERPROFILE%\.dotnet\tools\", "%HOME%/.dotnet/tools/", };
return globalPaths.Select(Environment.ExpandEnvironmentVariables).Contains(toolPath);
}
private void LogNewVersionInfo(UpdateChannel updateChannel, SemanticVersion latestVersion, string toolPath)
{
var toolPathArg = IsGlobalTool(toolPath) ? "-g" : $"--tool-path {toolPath}";
Logger.LogWarning($"ABP CLI has a newer {updateChannel.ToString().ToLowerInvariant()} version {latestVersion}, please update to get the latest features and fixes.");
Logger.LogWarning(string.Empty);
Logger.LogWarning("Update Command: ");
@ -179,18 +187,18 @@ namespace Volo.Abp.Cli
switch (updateChannel)
{
case UpdateChannel.Stable:
Logger.LogWarning("dotnet tool update -g Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool update {toolPathArg} Volo.Abp.Cli");
break;
case UpdateChannel.Prerelease:
Logger.LogWarning("dotnet tool uninstall -g Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool install -g Volo.Abp.Cli --version {latestVersion}");
Logger.LogWarning($"dotnet tool uninstall {toolPathArg} Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool install {toolPathArg} Volo.Abp.Cli --version {latestVersion}");
break;
case UpdateChannel.Nightly:
case UpdateChannel.Development:
Logger.LogWarning("dotnet tool uninstall -g Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool install -g Volo.Abp.Cli --add-source https://www.myget.org/F/abp-nightly/api/v3/index.json --version {latestVersion}");
Logger.LogWarning($"dotnet tool uninstall {toolPathArg} Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool install {toolPathArg} Volo.Abp.Cli --add-source https://www.myget.org/F/abp-nightly/api/v3/index.json --version {latestVersion}");
break;
default:
throw new ArgumentOutOfRangeException(nameof(updateChannel), updateChannel, null);

3
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs

@ -29,8 +29,7 @@ namespace Volo.Abp.Cli.ProjectModification
var csFiles = new DirectoryInfo(csprojFileDirectory)
.GetFiles("*.cs", SearchOption.AllDirectories)
.Where(f => !f.DirectoryName.StartsWith(binFile))
.Where(f => !f.DirectoryName.StartsWith(objFile))
.Where(f => f.DirectoryName != null && (!f.DirectoryName.StartsWith(binFile) || !f.DirectoryName.StartsWith(objFile)))
.Select(f => f.FullName)
.ToList();

30
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs

@ -83,8 +83,11 @@ namespace Volo.Abp.Cli.ProjectModification
public static string[] GetProjectFiles(string solutionFile)
{
var baseProjectFolder = GetBaseProjectFolder(solutionFile);
return Directory.GetFiles(baseProjectFolder, "*.csproj", SearchOption.AllDirectories);
return GetBaseProjectFolders(solutionFile)
.Select(baseProjectFolder =>
Directory.GetFiles(baseProjectFolder, "*.csproj", SearchOption.AllDirectories))
.SelectMany(files => files)
.ToArray();
}
public static string[] GetAssemblyNames(string[] projectFiles)
@ -118,16 +121,33 @@ namespace Volo.Abp.Cli.ProjectModification
return null;
}
private static string GetBaseProjectFolder(string solutionFile)
private static string[] GetBaseProjectFolders(string solutionFile)
{
var projectFolders = new List<string>();
var baseFolder = Path.GetDirectoryName(solutionFile);
if (baseFolder == null)
{
return projectFolders.ToArray();
}
var srcFolder = Path.Combine(baseFolder, "src");
if (Directory.Exists(srcFolder))
{
baseFolder = srcFolder;
projectFolders.Add(srcFolder);
}
var testFolder = Path.Combine(baseFolder, "test");
if (Directory.Exists(testFolder))
{
projectFolders.Add(testFolder);
}
if (!projectFolders.Any())
{
projectFolders.Add(baseFolder);
}
return baseFolder;
return projectFolders.ToArray();
}
}
}

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

@ -8,23 +8,22 @@ namespace Volo.Abp.Identity
public override void Define(ISettingDefinitionContext context)
{
context.Add(
new SettingDefinition(IdentitySettingNames.Password.RequiredLength, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Password.RequiredUniqueChars, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Password.RequireNonAlphanumeric, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Password.RequireLowercase, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Password.RequireUppercase, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Password.RequireDigit, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Password.RequiredLength),
new SettingDefinition(IdentitySettingNames.Password.RequiredUniqueChars),
new SettingDefinition(IdentitySettingNames.Password.RequireNonAlphanumeric),
new SettingDefinition(IdentitySettingNames.Password.RequireLowercase),
new SettingDefinition(IdentitySettingNames.Password.RequireUppercase),
new SettingDefinition(IdentitySettingNames.Password.RequireDigit),
new SettingDefinition(IdentitySettingNames.Lockout.AllowedForNewUsers, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Lockout.LockoutDuration, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Lockout.MaxFailedAccessAttempts, null, null, null, true),
new SettingDefinition(IdentitySettingNames.Lockout.AllowedForNewUsers),
new SettingDefinition(IdentitySettingNames.Lockout.LockoutDuration),
new SettingDefinition(IdentitySettingNames.Lockout.MaxFailedAccessAttempts),
new SettingDefinition(IdentitySettingNames.SignIn.RequireConfirmedEmail, null, null, null, true),
new SettingDefinition(IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber, null, null, null, true),
new SettingDefinition(IdentitySettingNames.SignIn.RequireConfirmedEmail),
new SettingDefinition(IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber),
new SettingDefinition(IdentitySettingNames.User.IsUserNameUpdateEnabled, "true"),
new SettingDefinition(IdentitySettingNames.User.IsEmailUpdateEnabled, "true")
new SettingDefinition(IdentitySettingNames.User.IsUserNameUpdateEnabled, "true", null, null, true),
new SettingDefinition(IdentitySettingNames.User.IsEmailUpdateEnabled, "true", null, null, true)
);
}

21
npm/ng-packs/.vscode/settings.json

@ -1,5 +1,22 @@
{
"search.exclude": {
"**/dist": true
}
}
},
"tslint.rulesDirectory": "../node_modules/codelyzer",
"typescript.tsdk": "../node_modules/typescript/lib",
"workbench.colorCustomizations": {
"activityBar.background": "#df7fa2",
"activityBar.foreground": "#15202b",
"activityBar.inactiveForeground": "#15202b99",
"activityBarBadge.background": "#c7ecb3",
"activityBarBadge.foreground": "#15202b",
"titleBar.activeBackground": "#d55685",
"titleBar.inactiveBackground": "#d5568599",
"titleBar.activeForeground": "#15202b",
"titleBar.inactiveForeground": "#15202b99",
"statusBar.background": "#d55685",
"statusBarItem.hoverBackground": "#c73169",
"statusBar.foreground": "#15202b"
},
"peacock.color": "#d55685"
}

254
npm/ng-packs/angular.json

@ -282,9 +282,257 @@
"packages/setting-management/tsconfig.lib.json",
"packages/setting-management/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
"exclude": ["**/node_modules/**"]
}
}
}
},
"setting-management-config": {
"projectType": "library",
"root": "packages/setting-management-config",
"sourceRoot": "packages/setting-management-config/src",
"prefix": "abp",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "packages/setting-management-config/tsconfig.lib.json",
"project": "packages/setting-management-config/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "packages/setting-management-config/src/test.ts",
"tsConfig": "packages/setting-management-config/tsconfig.spec.json",
"karmaConfig": "packages/setting-management-config/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"packages/setting-management-config/tsconfig.lib.json",
"packages/setting-management-config/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"identity-config": {
"projectType": "library",
"root": "packages/identity-config",
"sourceRoot": "packages/identity-config/src",
"prefix": "abp",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "packages/identity-config/tsconfig.lib.json",
"project": "packages/identity-config/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "packages/identity-config/src/test.ts",
"tsConfig": "packages/identity-config/tsconfig.spec.json",
"karmaConfig": "packages/identity-config/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["packages/identity-config/tsconfig.lib.json", "packages/identity-config/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
}
}
},
"account-config": {
"projectType": "library",
"root": "packages/account-config",
"sourceRoot": "packages/account-config/src",
"prefix": "abp",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "packages/account-config/tsconfig.lib.json",
"project": "packages/account-config/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "packages/account-config/src/test.ts",
"tsConfig": "packages/account-config/tsconfig.spec.json",
"karmaConfig": "packages/account-config/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["packages/account-config/tsconfig.lib.json", "packages/account-config/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
}
}
},
"tenant-management-config": {
"projectType": "library",
"root": "packages/tenant-management-config",
"sourceRoot": "packages/tenant-management-config/src",
"prefix": "abp",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "packages/tenant-management-config/tsconfig.lib.json",
"project": "packages/tenant-management-config/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "packages/tenant-management-config/src/test.ts",
"tsConfig": "packages/tenant-management-config/tsconfig.spec.json",
"karmaConfig": "packages/tenant-management-config/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"packages/tenant-management-config/tsconfig.lib.json",
"packages/tenant-management-config/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"dev-app": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "apps/dev-app",
"sourceRoot": "apps/dev-app/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/dev-app",
"index": "apps/dev-app/src/index.html",
"main": "apps/dev-app/src/main.ts",
"polyfills": "apps/dev-app/src/polyfills.ts",
"tsConfig": "apps/dev-app/tsconfig.app.json",
"aot": false,
"assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"],
"styles": [
"apps/dev-app/src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/font-awesome/css/font-awesome.min.css",
"node_modules/primeng/resources/themes/nova-light/theme.css",
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/dev-app/src/environments/environment.ts",
"with": "apps/dev-app/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "dev-app:build"
},
"configurations": {
"production": {
"browserTarget": "dev-app:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "dev-app:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/dev-app/src/test.ts",
"polyfills": "apps/dev-app/src/polyfills.ts",
"tsConfig": "apps/dev-app/tsconfig.spec.json",
"karmaConfig": "apps/dev-app/karma.conf.js",
"assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"],
"styles": [
"apps/dev-app/src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/font-awesome/css/font-awesome.min.css",
"node_modules/primeng/resources/themes/nova-light/theme.css",
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"apps/dev-app/tsconfig.app.json",
"apps/dev-app/tsconfig.spec.json",
"apps/dev-app/e2e/tsconfig.json"
],
"exclude": ["**/node_modules/**"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "apps/dev-app/e2e/protractor.conf.js",
"devServerTarget": "dev-app:serve"
},
"configurations": {
"production": {
"devServerTarget": "dev-app:serve:production"
}
}
}
}

12
npm/ng-packs/apps/dev-app/browserslist

@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

30
npm/ng-packs/apps/dev-app/e2e/protractor.conf.js

@ -0,0 +1,30 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: ['./src/**/*.e2e-spec.ts'],
capabilities: {
browserName: 'chrome',
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {},
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json'),
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
},
};

28
npm/ng-packs/apps/dev-app/e2e/src/app.e2e-spec.ts

@ -0,0 +1,28 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('dev-app app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser
.manage()
.logs()
.get(logging.Type.BROWSER);
expect(logs).not.toContain(
jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry),
);
});
});

11
npm/ng-packs/apps/dev-app/e2e/src/app.po.ts

@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
}

9
npm/ng-packs/apps/dev-app/e2e/tsconfig.json

@ -0,0 +1,9 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": ["jasmine", "jasminewd2", "node"]
}
}

32
npm/ng-packs/apps/dev-app/karma.conf.js

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/dev-app'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true,
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true,
});
};

34
npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts

@ -0,0 +1,34 @@
import { ABP } from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
data: {
routes: {
name: '::Menu:Home',
} as ABP.Route,
},
},
{
path: 'account',
loadChildren: () => import('./lazy-libs/account-wrapper.module').then(m => m.AccountWrapperModule),
},
{
path: 'identity',
loadChildren: () => import('./lazy-libs/identity-wrapper.module').then(m => m.IdentityWrapperModule),
},
{
path: 'tenant-management',
loadChildren: () =>
import('./lazy-libs/tenant-management-wrapper.module').then(m => m.TenantManagementWrapperModule),
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}

10
npm/ng-packs/apps/dev-app/src/app/app.component.ts

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<abp-loader-bar></abp-loader-bar>
<router-outlet></router-outlet>
`,
})
export class AppComponent {}

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

@ -0,0 +1,43 @@
import { CoreModule } from '@abp/ng.core';
import { LAYOUTS } from '@abp/ng.theme.basic';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
import { NgxsModule } from '@ngxs/store';
import { OAuthModule } from 'angular-oauth2-oidc';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { AccountConfigModule } from '@abp/ng.account.config';
import { IdentityConfigModule } from '@abp/ng.identity.config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
@NgModule({
declarations: [AppComponent],
imports: [
CoreModule.forRoot({
environment,
requirements: {
layouts: LAYOUTS,
},
}),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot({ redirectUrl: '/' }),
IdentityConfigModule,
TenantManagementConfigModule,
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
SharedModule,
OAuthModule.forRoot(),
NgxsModule.forRoot([]),
NgxsReduxDevtoolsPluginModule.forRoot(),
],
bootstrap: [AppComponent],
})
export class AppModule {}

18
npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home.component';
import { ApplicationLayoutComponent } from '@abp/ng.theme.basic';
const routes: Routes = [
{
path: '',
component: ApplicationLayoutComponent,
children: [{ path: '', component: HomeComponent }],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class HomeRoutingModule {}

15
npm/ng-packs/apps/dev-app/src/app/home/home.component.html

@ -0,0 +1,15 @@
<div class="card">
<div class="card-header">{{ '::Welcome' | abpLocalization }}</div>
<div class="card-body">
<p>
{{ '::LongWelcomeMessage' | abpLocalization }}
</p>
<p *ngIf="!hasLoggedIn">
<a routerLink="/account/login" [state]="{ redirectUrl: '/' }" class="btn btn-primary" role="button"
><i class="fa fa-sign-in"></i>{{ 'AbpIdentity::Login' | abpLocalization }}</a
>
</p>
<hr />
<p class="text-right"><a href="https://abp.io?ref=tmpl" target="_blank">abp.io</a></p>
</div>
</div>

14
npm/ng-packs/apps/dev-app/src/app/home/home.component.ts

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
get hasLoggedIn(): boolean {
return this.oAuthService.hasValidAccessToken();
}
constructor(private oAuthService: OAuthService) {}
}

10
npm/ng-packs/apps/dev-app/src/app/home/home.module.ts

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component';
@NgModule({
declarations: [HomeComponent],
imports: [SharedModule, HomeRoutingModule],
})
export class HomeModule {}

7
npm/ng-packs/apps/dev-app/src/app/lazy-libs/account-wrapper.module.ts

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { AccountModule } from '@abp/ng.account';
@NgModule({
imports: [AccountModule],
})
export class AccountWrapperModule {}

7
npm/ng-packs/apps/dev-app/src/app/lazy-libs/identity-wrapper.module.ts

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { IdentityModule } from '@abp/ng.identity';
@NgModule({
imports: [IdentityModule],
})
export class IdentityWrapperModule {}

7
npm/ng-packs/apps/dev-app/src/app/lazy-libs/tenant-management-wrapper.module.ts

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { TenantManagementModule } from '@abp/ng.tenant-management';
@NgModule({
imports: [TenantManagementModule],
})
export class TenantManagementWrapperModule {}

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

@ -0,0 +1,14 @@
import { CoreModule } from '@abp/ng.core';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgModule } from '@angular/core';
import { ThemeBasicModule } from '@abp/ng.theme.basic';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { TableModule } from 'primeng/table';
@NgModule({
declarations: [],
imports: [CoreModule, ThemeSharedModule, ThemeBasicModule, TableModule, NgbDropdownModule],
exports: [CoreModule, ThemeSharedModule, ThemeBasicModule, TableModule, NgbDropdownModule],
providers: [],
})
export class SharedModule {}

0
npm/ng-packs/apps/dev-app/src/assets/.gitkeep

25
npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts

@ -0,0 +1,25 @@
export const environment = {
production: true,
hmr: false,
application: {
name: 'MyProjectName',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44305',
clientId: 'MyProjectName_App',
dummyClientSecret: '1q2w3e*',
scope: 'MyProjectName',
showDebugInformation: true,
oidc: false,
requireHttps: true,
},
apis: {
default: {
url: 'https://localhost:44305',
},
},
localization: {
defaultResourceName: 'MyProjectName',
},
};

25
npm/ng-packs/apps/dev-app/src/environments/environment.ts

@ -0,0 +1,25 @@
export const environment = {
production: false,
hmr: false,
application: {
name: 'MyProjectName',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44305',
clientId: 'MyProjectName_App',
dummyClientSecret: '1q2w3e*',
scope: 'MyProjectName',
showDebugInformation: true,
oidc: false,
requireHttps: true,
},
apis: {
default: {
url: 'https://localhost:44305',
},
},
localization: {
defaultResourceName: 'MyProjectName',
},
};

BIN
npm/ng-packs/apps/dev-app/src/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

13
npm/ng-packs/apps/dev-app/src/index.html

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>DevApp</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>

13
npm/ng-packs/apps/dev-app/src/main.ts

@ -0,0 +1,13 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));

62
npm/ng-packs/apps/dev-app/src/polyfills.ts

@ -0,0 +1,62 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

1
npm/ng-packs/apps/dev-app/src/styles.scss

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

14
npm/ng-packs/apps/dev-app/src/test.ts

@ -0,0 +1,14 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

38
npm/ng-packs/apps/dev-app/tsconfig.app.json

@ -0,0 +1,38 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/app",
"types": [],
"paths": {
"@abp/ng.core": ["packages/core/src/public-api.ts"],
"@abp/ng.core/*": ["packages/core/src/lib/*"],
// "@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"],
// "@abp/ng.theme.shared/*": ["packages/theme-shared/src/lib/*"],
"@abp/ng.theme.basic": ["packages/theme-basic/src/public-api.ts"],
"@abp/ng.theme.basic/*": ["packages/theme-basic/src/lib/*"],
"@abp/ng.account": ["packages/account/src/public-api.ts"],
"@abp/ng.account/*": ["packages/account/src/lib/*"],
"@abp/ng.account.config": ["packages/account-config/src/public-api.ts"],
"@abp/ng.account.config/*": ["packages/account-config/src/lib/*"],
"@abp/ng.identity": ["packages/identity/src/public-api.ts"],
"@abp/ng.identity/*": ["packages/identity/src/lib/*"],
"@abp/ng.identity.config": ["packages/identity-config/src/public-api.ts"],
"@abp/ng.identity.config/*": ["packages/identity-config/src/lib/*"],
"@abp/ng.tenant-management": ["packages/tenant-management/src/public-api.ts"],
"@abp/ng.tenant-management/*": ["packages/tenant-management/src/lib/*"],
"@abp/ng.tenant-management.config": ["packages/tenant-management-config/src/public-api.ts"],
"@abp/ng.tenant-management.config/*": ["packages/tenant-management-config/src/lib/*"],
"@abp/ng.setting-management": ["packages/setting-management/src/public-api.ts"],
"@abp/ng.setting-management/*": ["packages/setting-management/src/lib/*"],
"@abp/ng.setting-management.config": ["packages/setting-management-config/src/public-api.ts"],
"@abp/ng.setting-management.config/*": ["packages/setting-management-config/src/lib/*"],
"@abp/ng.permission-management": ["packages/permission-management/src/public-api.ts"],
"@abp/ng.permission-management/*": ["packages/permission-management/src/lib/*"],
"@abp/ng.feature-management": ["packages/feature-management/src/public-api.ts"],
"@abp/ng.feature-management/*": ["packages/feature-management/src/lib/*"]
}
},
"files": ["src/main.ts", "src/polyfills.ts"],
"include": ["src/**/*.ts"],
"exclude": ["src/test.ts", "src/**/*.spec.ts"]
}

9
npm/ng-packs/apps/dev-app/tsconfig.spec.json

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": ["jasmine", "node"]
},
"files": ["src/test.ts", "src/polyfills.ts"],
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}

7
npm/ng-packs/apps/dev-app/tslint.json

@ -0,0 +1,7 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [true, "attribute", "app", "camelCase"],
"component-selector": [true, "element", "app", "kebab-case"]
}
}

87
npm/ng-packs/package.json

@ -1,41 +1,48 @@
{
"scripts": {
"start": "ng serve dev-app",
"ng": "ng",
"symlink": "symlink",
"abpng": "abpng"
"abpng": "abpng",
"commit": "git-cz && node scripts/push.js",
"lint": "ng lint --fix"
},
"devDependencies": {
"@abp/ng.core": "^0.8.3",
"@abp/ng.feature-management": "^0.8.3",
"@abp/ng.permission-management": "^0.8.3",
"@abp/ng.theme.shared": "^0.8.3",
"@angular-devkit/build-angular": "~0.803.5",
"@angular-devkit/build-ng-packagr": "~0.803.5",
"@angular/animations": "~8.2.7",
"@angular/cdk": "^8.0.1",
"@angular/cli": "~8.3.5",
"@angular/common": "~8.1.2",
"@angular/compiler": "~8.2.7",
"@angular/compiler-cli": "~8.2.7",
"@angular/core": "~8.1.2",
"@angular/forms": "~8.1.2",
"@angular/language-service": "~8.2.7",
"@angular/platform-browser": "~8.2.7",
"@angular/platform-browser-dynamic": "~8.2.7",
"@angular/router": "~8.1.2",
"@abp/ng.core": "^0.9.0",
"@abp/ng.feature-management": "^0.9.0",
"@abp/ng.permission-management": "^0.9.0",
"@abp/ng.theme.shared": "^0.9.0",
"@angular-devkit/build-angular": "~0.803.6",
"@angular-devkit/build-ng-packagr": "~0.803.6",
"@angular/animations": "~8.2.8",
"@angular/cdk": "^8.2.2",
"@angular/cli": "~8.3.6",
"@angular/common": "~8.2.8",
"@angular/compiler": "~8.2.8",
"@angular/compiler-cli": "~8.2.8",
"@angular/core": "~8.2.8",
"@angular/forms": "~8.2.8",
"@angular/language-service": "~8.2.8",
"@angular/platform-browser": "~8.2.8",
"@angular/platform-browser-dynamic": "~8.2.8",
"@angular/router": "~8.2.8",
"@ng-bootstrap/ng-bootstrap": "^5.1.0",
"@ngneat/spectator": "^4.3.2",
"@ngx-validate/core": "^0.0.5",
"@ngxs/router-plugin": "^3.5.0",
"@ngxs/storage-plugin": "^3.5.0",
"@ngxs/store": "^3.5.0",
"@ngxs/devtools-plugin": "^3.5.1",
"@ngxs/router-plugin": "^3.5.1",
"@ngxs/storage-plugin": "^3.5.1",
"@ngxs/store": "^3.5.1",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"angular-oauth2-oidc": "^8.0.1",
"bootstrap": "^4.3.1",
"chart.js": "^2.8.0",
"codelyzer": "^5.0.0",
"codelyzer": "^5.1.2",
"cz-conventional-changelog": "3.0.2",
"font-awesome": "^4.7.0",
"husky": "^3.0.7",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"just-clone": "3.1.0",
@ -45,19 +52,45 @@
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"lint-staged": "^9.4.0",
"ng-packagr": "^5.5.0",
"ngx-perfect-scrollbar": "^8.0.0",
"primeicons": "^1.0.0",
"ngxs-reset-plugin": "^1.2.0",
"prettier": "^1.18.2",
"primeicons": "^2.0.0",
"primeng": "^8.0.0",
"protractor": "~5.4.0",
"rxjs": "~6.4.0",
"snq": "^1.0.3",
"symlink-manager": "^1.3.0",
"ts-node": "~7.0.0",
"tsickle": "^0.35.0",
"tslib": "^1.9.0",
"tslint": "~5.15.0",
"tsickle": "^0.37.0",
"tslint": "~5.20.0",
"typescript": "~3.5.3",
"zone.js": "~0.9.1"
},
"dependencies": {
"tslib": "^1.10.0"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,js,html,json,md,yaml,yml}": [
"prettier --write",
"git add"
],
"*.ts": [
"prettier --write",
"tslint --fix",
"git add"
]
}
}

1
npm/ng-packs/packages/account-config/README.md

@ -0,0 +1 @@
# @abp/ng.account.config

32
npm/ng-packs/packages/account-config/karma.conf.js

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/account-config'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

7
npm/ng-packs/packages/account-config/ng-package.json

@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/account-config",
"lib": {
"entryFile": "src/public-api.ts"
}
}

7
npm/ng-packs/packages/account-config/package.json

@ -0,0 +1,7 @@
{
"name": "@abp/ng.account.config",
"version": "0.0.1",
"publishConfig": {
"access": "public"
}
}

37
npm/ng-packs/packages/account-config/src/lib/account-config.module.ts

@ -0,0 +1,37 @@
import { CoreModule, noop } from '@abp/ng.core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { APP_INITIALIZER, InjectionToken, ModuleWithProviders, NgModule } from '@angular/core';
import { AccountConfigService } from './services/account-config.service';
export interface AccountConfigOptions {
redirectUrl?: string;
}
export function accountOptionsFactory(options: AccountConfigOptions) {
return {
redirectUrl: '/',
...options,
};
}
export const ACCOUNT_OPTIONS = new InjectionToken('ACCOUNT_OPTIONS');
@NgModule({
imports: [CoreModule, ThemeSharedModule],
providers: [{ provide: APP_INITIALIZER, multi: true, deps: [AccountConfigService], useFactory: noop }],
})
export class AccountConfigModule {
static forRoot(options = {} as AccountConfigOptions): ModuleWithProviders {
return {
ngModule: AccountConfigModule,
providers: [
{ provide: ACCOUNT_OPTIONS, useValue: options },
{
provide: 'ACCOUNT_OPTIONS',
useFactory: accountOptionsFactory,
deps: [ACCOUNT_OPTIONS],
},
],
};
}
}

21
npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts

@ -0,0 +1,21 @@
import { ABP_ROUTES, eLayoutType, RestService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root',
})
export class AccountConfigService {
constructor(private router: Router, private restService: RestService) {
ABP_ROUTES.push({
name: 'AbpAccount::Menu:Account',
path: 'account',
invisible: true,
layout: eLayoutType.application,
children: [
{ path: 'login', name: 'AbpAccount::Login', order: 1 },
{ path: 'register', name: 'AbpAccount::Register', order: 2 },
],
});
}
}

2
npm/ng-packs/packages/account-config/src/public-api.ts

@ -0,0 +1,2 @@
export * from './lib/services/account-config.service';
export * from './lib/account-config.module';

21
npm/ng-packs/packages/account-config/src/test.ts

@ -0,0 +1,21 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

26
npm/ng-packs/packages/account-config/tsconfig.lib.json

@ -0,0 +1,26 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

17
npm/ng-packs/packages/account-config/tsconfig.spec.json

@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

17
npm/ng-packs/packages/account-config/tslint.json

@ -0,0 +1,17 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"abp",
"camelCase"
],
"component-selector": [
true,
"element",
"abp",
"kebab-case"
]
}
}

4
npm/ng-packs/packages/account/src/lib/account.module.ts

@ -18,6 +18,10 @@ import { ACCOUNT_OPTIONS, optionsFactory } from './tokens/options.token';
})
export class AccountModule {}
/**
*
* @deprecated since version 0.9
*/
export function AccountProviders(options = {} as Options): Provider[] {
return [
{ provide: ACCOUNT_OPTIONS, useValue: options },

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

@ -9,10 +9,14 @@ import { AccountService } from '../../services/account.service';
@Component({
selector: 'abp-tenant-box',
templateUrl: './tenant-box.component.html',
templateUrl: './tenant-box.component.html'
})
export class TenantBoxComponent implements OnInit {
constructor(private store: Store, private toasterService: ToasterService, private accountService: AccountService) {}
constructor(
private store: Store,
private toasterService: ToasterService,
private accountService: AccountService
) {}
tenant = {} as ABP.BasicItem;
@ -21,7 +25,9 @@ export class TenantBoxComponent implements OnInit {
isModalVisible: boolean;
ngOnInit() {
this.tenant = this.store.selectSnapshot(SessionState.getTenant) || ({} as ABP.BasicItem);
this.tenant =
this.store.selectSnapshot(SessionState.getTenant) ||
({} as ABP.BasicItem);
this.tenantName = this.tenant.name || '';
}
@ -37,24 +43,31 @@ export class TenantBoxComponent implements OnInit {
take(1),
catchError(err => {
this.toasterService.error(
snq(() => err.error.error_description, 'AbpUi::DefaultErrorMessage'),
'AbpUi::Error',
snq(
() => err.error.error_description,
'AbpUi::DefaultErrorMessage'
),
'AbpUi::Error'
);
return throwError(err);
}),
})
)
.subscribe(({ success, tenantId }) => {
if (success) {
this.tenant = {
id: tenantId,
name: this.tenant.name,
name: this.tenant.name
};
this.tenantName = this.tenant.name;
this.isModalVisible = false;
} else {
this.toasterService.error(`AbpUiMultiTenancy::GivenTenantIsNotAvailable`, 'AbpUi::Error', {
messageLocalizationParams: [this.tenant.name],
});
this.toasterService.error(
'AbpUiMultiTenancy::GivenTenantIsNotAvailable',
'AbpUi::Error',
{
messageLocalizationParams: [this.tenant.name]
}
);
this.tenant = {} as ABP.BasicItem;
}
this.store.dispatch(new SetTenant(success ? this.tenant : null));

4
npm/ng-packs/packages/account/src/lib/constants/routes.ts

@ -1,5 +1,9 @@
import { ABP, eLayoutType } from '@abp/ng.core';
/**
*
* @deprecated since version 0.9
*/
export const ACCOUNT_ROUTES = {
routes: [
{

8
npm/ng-packs/packages/account/src/lib/services/account.service.ts

@ -4,7 +4,7 @@ import { RestService, Rest } from '@abp/ng.core';
import { RegisterResponse, RegisterRequest, TenantIdResponse } from '../models';
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class AccountService {
constructor(private rest: RestService) {}
@ -12,7 +12,7 @@ export class AccountService {
findTenant(tenantName: string): Observable<TenantIdResponse> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`,
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`
};
return this.rest.request<null, TenantIdResponse>(request);
@ -21,8 +21,8 @@ export class AccountService {
register(body: RegisterRequest): Observable<RegisterResponse> {
const request: Rest.Request<RegisterRequest> = {
method: 'POST',
url: `/api/account/register`,
body,
url: '/api/account/register',
body
};
return this.rest.request<RegisterRequest, RegisterResponse>(request, { skipHandleError: true });

8
npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts

@ -1,13 +1,11 @@
import { ControlValueAccessor } from '@angular/forms';
import { ChangeDetectorRef, Component, Injector, Input, Type } from '@angular/core';
@Component({ template: '' })
@Component({ selector: 'abp-abstract-ng-model', template: '' })
export class AbstractNgModelComponent<T = any> implements ControlValueAccessor {
@Input()
disabled: boolean;
@Input() disabled: boolean;
@Input()
set value(value: T) {
@Input() set value(value: T) {
this._value = value;
this.notifyValueChange();
}

8
npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts

@ -13,21 +13,19 @@ import { takeUntilDestroy } from '../utils/rxjs-utils';
selector: 'abp-dynamic-layout',
template: `
<ng-container *ngTemplateOutlet="layout ? componentOutlet : routerOutlet"></ng-container>
<ng-template #routerOutlet><router-outlet></router-outlet></ng-template>
<ng-template #componentOutlet><ng-container *ngComponentOutlet="layout"></ng-container></ng-template>
`,
`
})
export class DynamicLayoutComponent implements OnDestroy {
@Select(ConfigState.getOne('requirements'))
requirements$: Observable<Config.Requirements>;
@Select(ConfigState.getOne('requirements')) requirements$: Observable<Config.Requirements>;
layout: Type<any>;
constructor(private router: Router, private route: ActivatedRoute, private store: Store) {
const {
requirements: { layouts },
routes,
routes
} = this.store.selectSnapshot(ConfigState.getAll);
if ((this.route.snapshot.data || {}).layout) {

5
npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts

@ -1,11 +1,12 @@
import { Directive, ElementRef, Input, AfterViewInit } from '@angular/core';
@Directive({
selector: '[autofocus]',
// tslint:disable-next-line: directive-selector
selector: '[autofocus]'
})
export class AutofocusDirective implements AfterViewInit {
@Input('autofocus')
delay: number = 0;
delay = 0;
constructor(private elRef: ElementRef) {}

9
npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts

@ -4,12 +4,13 @@ import { debounceTime } from 'rxjs/operators';
import { takeUntilDestroy } from '@ngx-validate/core';
@Directive({
selector: '[input.debounce]',
// tslint:disable-next-line: directive-selector
selector: '[input.debounce]'
})
export class InputEventDebounceDirective implements OnInit {
@Input() debounce: number = 300;
@Input() debounce = 300;
@Output('input.debounce') debounceEvent = new EventEmitter<Event>();
@Output('input.debounce') readonly debounceEvent = new EventEmitter<Event>();
constructor(private renderer: Renderer2, private el: ElementRef) {}
@ -17,7 +18,7 @@ export class InputEventDebounceDirective implements OnInit {
fromEvent(this.el.nativeElement, 'input')
.pipe(
debounceTime(this.debounce),
takeUntilDestroy(this),
takeUntilDestroy(this)
)
.subscribe((event: Event) => {
this.debounceEvent.emit(event);

11
npm/ng-packs/packages/core/src/lib/directives/for.directive.ts

@ -9,7 +9,7 @@ import {
OnChanges,
TemplateRef,
TrackByFunction,
ViewContainerRef,
ViewContainerRef
} from '@angular/core';
import compare from 'just-compare';
import clone from 'just-clone';
@ -25,7 +25,7 @@ class RecordView {
}
@Directive({
selector: '[abpFor]',
selector: '[abpFor]'
})
export class ForDirective implements OnChanges {
@Input('abpForOf')
@ -67,7 +67,7 @@ export class ForDirective implements OnChanges {
constructor(
private tempRef: TemplateRef<AbpForContext>,
private vcRef: ViewContainerRef,
private differs: IterableDiffers,
private differs: IterableDiffers
) {}
private iterateOverAppliedOperations(changes: IterableChanges<any>) {
@ -78,7 +78,7 @@ export class ForDirective implements OnChanges {
const view = this.vcRef.createEmbeddedView(
this.tempRef,
new AbpForContext(null, -1, -1, this.items),
currentIndex,
currentIndex
);
rw.push(new RecordView(record, view));
@ -113,8 +113,11 @@ export class ForDirective implements OnChanges {
private projectItems(items: any[]): void {
if (!items.length && this.emptyRef) {
this.vcRef.clear();
// tslint:disable-next-line: no-unused-expression
this.vcRef.createEmbeddedView(this.emptyRef).rootNodes;
this.isShowEmptyRef = true;
this.differ = null;
return;
}

16
npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts

@ -7,7 +7,7 @@ import {
OnDestroy,
OnInit,
Output,
Self,
Self
} from '@angular/core';
import { FormControl, FormGroup, FormGroupDirective } from '@angular/forms';
import { fromEvent } from 'rxjs';
@ -17,21 +17,21 @@ import { takeUntilDestroy } from '../utils';
type Controls = { [key: string]: FormControl } | FormGroup[];
@Directive({
selector: 'form[ngSubmit][formGroup]',
// tslint:disable-next-line: directive-selector
selector: 'form[ngSubmit][formGroup]'
})
export class FormSubmitDirective implements OnInit, OnDestroy {
@Input()
notValidateOnSubmit: string | boolean;
@Output()
ngSubmit = new EventEmitter();
@Output() readonly ngSubmit = new EventEmitter();
executedNgSubmit: boolean = false;
executedNgSubmit = false;
constructor(
@Self() private formGroupDirective: FormGroupDirective,
private host: ElementRef<HTMLFormElement>,
private cdRef: ChangeDetectorRef,
private cdRef: ChangeDetectorRef
) {}
ngOnInit() {
@ -44,7 +44,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy {
.pipe(
debounceTime(200),
filter((key: KeyboardEvent) => key && key.key === 'Enter'),
takeUntilDestroy(this),
takeUntilDestroy(this)
)
.subscribe(() => {
if (!this.executedNgSubmit) {
@ -57,7 +57,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy {
fromEvent(this.host.nativeElement, 'submit')
.pipe(
takeUntilDestroy(this),
filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string'),
filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string')
)
.subscribe(() => {
if (!this.executedNgSubmit) {

5
npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts

@ -3,10 +3,11 @@ import { fromEvent } from 'rxjs';
import { takeUntilDestroy } from '@ngx-validate/core';
@Directive({
selector: '[click.stop]',
// tslint:disable-next-line: directive-selector
selector: '[click.stop]'
})
export class ClickEventStopPropagationDirective implements OnInit {
@Output('click.stop') stopPropEvent = new EventEmitter<MouseEvent>();
@Output('click.stop') readonly stopPropEvent = new EventEmitter<MouseEvent>();
constructor(private renderer: Renderer2, private el: ElementRef) {}

10
npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts

@ -3,14 +3,14 @@ import { Subject } from 'rxjs';
import snq from 'snq';
@Directive({
selector: '[abpVisibility]',
selector: '[abpVisibility]'
})
export class VisibilityDirective implements AfterViewInit {
@Input('abpVisibility')
focusedElement: HTMLElement;
@Input()
mutationObserverEnabled: boolean = true;
mutationObserverEnabled = true;
completed$ = new Subject<boolean>();
@ -25,7 +25,7 @@ export class VisibilityDirective implements AfterViewInit {
const htmlNodes = snq(
() => Array.from(mutation.target.childNodes).filter(node => node instanceof HTMLElement),
[],
[]
);
if (!htmlNodes.length) {
@ -40,13 +40,13 @@ export class VisibilityDirective implements AfterViewInit {
});
observer.observe(this.focusedElement, {
childList: true,
childList: true
});
} else {
setTimeout(() => {
const htmlNodes = snq(
() => Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement),
[],
[]
);
if (!htmlNodes.length) this.removeFromDOM();

3
npm/ng-packs/packages/core/src/lib/enums/common.ts

@ -2,5 +2,8 @@ export const enum eLayoutType {
account = 'account',
application = 'application',
empty = 'empty',
/**
* @deprecated since version 0.9.0
*/
setting = 'setting',
}

4
npm/ng-packs/packages/core/src/lib/models/common.ts

@ -43,4 +43,8 @@ export namespace ABP {
id: string;
name: string;
}
export interface Dictionary<T = any> {
[key: string]: T;
}
}

5
npm/ng-packs/packages/core/src/lib/models/config.ts

@ -30,4 +30,9 @@ export namespace Config {
export interface Requirements {
layouts: Type<any>[];
}
export interface LocalizationWithDefault {
key: string;
defaultValue: string;
}
}

2
npm/ng-packs/packages/core/src/lib/pipes/index.ts

@ -1,2 +1,2 @@
export * from './localization.pipe';
export * from "./sort.pipe";
export * from './sort.pipe';

45
npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts

@ -1,45 +1,20 @@
import { Pipe, PipeTransform, OnDestroy } from '@angular/core';
import { Pipe, PipeTransform } from '@angular/core';
import { Store } from '@ngxs/store';
import { Config } from '../models';
import { ConfigState } from '../states';
import { takeUntilDestroy } from '../utils';
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
@Pipe({
name: 'abpLocalization',
pure: false, // required to update the value
})
export class LocalizationPipe implements PipeTransform, OnDestroy {
initialValue: string = '';
value: string;
destroy$ = new Subject();
export class LocalizationPipe implements PipeTransform {
constructor(private store: Store) {}
transform(value: string = '', ...interpolateParams: string[]): string {
if (this.initialValue !== value) {
this.initialValue = value;
this.destroy$.next();
this.store
.select(
ConfigState.getCopy(
value,
...interpolateParams.reduce((acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), []),
),
)
.pipe(
takeUntil(this.destroy$),
takeUntilDestroy(this),
distinctUntilChanged(),
)
.subscribe(copy => (this.value = copy));
}
return this.value;
transform(value: string | Config.LocalizationWithDefault = '', ...interpolateParams: string[]): string {
return this.store.selectSnapshot(
ConfigState.getLocalization(
value,
...interpolateParams.reduce((acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), []),
),
);
}
ngOnDestroy() {}
}

17
npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts

@ -1,13 +1,14 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'abpSort',
pure: false
name: 'abpSort',
// tslint:disable-next-line: no-pipe-impure
pure: false
})
export class SortPipe implements PipeTransform {
transform(value: any[], sortOrder: string): any {
sortOrder = sortOrder.toLowerCase();
if(sortOrder === "desc") return value.reverse();
else return value;
}
}
transform(value: any[], sortOrder: string): any {
sortOrder = sortOrder.toLowerCase();
if (sortOrder === 'desc') return value.reverse();
else return value;
}
}

27
npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts

@ -8,9 +8,11 @@ import clone from 'just-clone';
export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN_OPTIONS');
export let ABP_ROUTES = [] as ABP.FullRoute[];
@Injectable()
export class ConfigPlugin implements NgxsPlugin {
private initialized: boolean = false;
private initialized = false;
constructor(@Inject(NGXS_CONFIG_PLUGIN_OPTIONS) private options: ABP.Root, private router: Router) {}
@ -18,9 +20,11 @@ export class ConfigPlugin implements NgxsPlugin {
const matches = actionMatcher(event);
const isInitAction = matches(InitState) || matches(UpdateState);
// const layouts = snq(() => this.options.requirements.layouts.filter(layout => layout instanceof Type), []);
if (isInitAction && !this.initialized) {
let { routes, wrappers } = transformRoutes(this.router.config);
const transformedRoutes = transformRoutes(this.router.config);
let { routes } = transformedRoutes;
const { wrappers } = transformedRoutes;
routes = organizeRoutes(routes, wrappers);
const flattedRoutes = flatRoutes(clone(routes));
state = setValue(state, 'ConfigState', {
@ -38,27 +42,32 @@ export class ConfigPlugin implements NgxsPlugin {
}
function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): any {
/**
*
* @deprecated since version 0.9.0
*/
const abpRoutes: ABP.FullRoute[] = routes
.filter(route => {
return snq(() => route.data.routes.routes.find(r => r.path === route.path), false);
})
.reduce((acc, val) => [...acc, ...val.data.routes.routes], []);
// tslint:disable-next-line: deprecation
ABP_ROUTES = [...ABP_ROUTES, ...abpRoutes];
wrappers = abpRoutes.filter(ar => ar.wrapper);
wrappers = ABP_ROUTES.filter(ar => ar.wrapper);
const transformed = [] as ABP.FullRoute[];
routes
.filter(route => (route.data || {}).routes && (route.component || route.loadChildren))
.filter(route => route.component || route.loadChildren)
.forEach(route => {
const abpPackage = abpRoutes.find(
abp => abp.path.toLowerCase() === route.path.toLowerCase() && snq(() => route.data.routes.routes.length, false),
);
const abpPackage = ABP_ROUTES.find(abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper);
const { length } = transformed;
if (abpPackage) {
transformed.push(abpPackage);
}
if (transformed.length === length) {
if (transformed.length === length && (route.data || {}).routes) {
transformed.push({
...route.data.routes,
path: route.path,

3
npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts

@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { ApplicationConfiguration, Rest } from '../models';
import { Rest } from '../models/rest';
import { ApplicationConfiguration } from '../models/application-configuration';
import { RestService } from './rest.service';
@Injectable({

19
npm/ng-packs/packages/core/src/lib/services/localization.service.ts

@ -1,9 +1,8 @@
import { Injectable, Optional, SkipSelf, NgZone } from '@angular/core';
import { Injectable, NgZone, Optional, SkipSelf } from '@angular/core';
import { ActivatedRouteSnapshot, Router } from '@angular/router';
import { Actions, Store } from '@ngxs/store';
import { Store } from '@ngxs/store';
import { noop, Observable } from 'rxjs';
import { ConfigState } from '../states/config.state';
import { SessionState } from '../states/session.state';
import { registerLocale } from '../utils/initial-utils';
type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean;
@ -11,14 +10,13 @@ type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSna
@Injectable({ providedIn: 'root' })
export class LocalizationService {
get currentLang(): string {
return this.store.selectSnapshot(SessionState.getLanguage);
return this.store.selectSnapshot(state => state.SessionState.language);
}
constructor(
private store: Store,
private router: Router,
private ngZone: NgZone,
private actions: Actions,
@Optional()
@SkipSelf()
otherInstance: LocalizationService,
@ -26,13 +24,12 @@ export class LocalizationService {
if (otherInstance) throw new Error('LocaleService should have only one instance.');
}
private setRouteReuse(reuse: ShouldReuseRoute) {
setRouteReuse(reuse: ShouldReuseRoute) {
this.router.routeReuseStrategy.shouldReuseRoute = reuse;
}
registerLocale(locale: string) {
const { shouldReuseRoute } = this.router.routeReuseStrategy;
this.setRouteReuse(() => false);
this.router.navigated = false;
@ -44,11 +41,11 @@ export class LocalizationService {
});
}
get(keys: string, ...interpolateParams: string[]): Observable<string> {
return this.store.select(ConfigState.getCopy(keys, ...interpolateParams));
get(key: string, ...interpolateParams: string[]): Observable<string> {
return this.store.select(ConfigState.getLocalization(key, ...interpolateParams));
}
instant(keys: string, ...interpolateParams: string[]): string {
return this.store.selectSnapshot(ConfigState.getCopy(keys, ...interpolateParams));
instant(key: string, ...interpolateParams: string[]): string {
return this.store.selectSnapshot(ConfigState.getLocalization(key, ...interpolateParams));
}
}

6
npm/ng-packs/packages/core/src/lib/services/rest.service.ts

@ -1,11 +1,11 @@
import { HttpClient, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Store } from '@ngxs/store';
import { NEVER, Observable, throwError } from 'rxjs';
import { Observable, throwError } from 'rxjs';
import { catchError, take } from 'rxjs/operators';
import { RestOccurError } from '../actions/rest.actions';
import { Rest } from '../models/rest';
import { ConfigState } from '../states';
import { RestOccurError } from '../actions';
import { ConfigState } from '../states/config.state';
@Injectable({
providedIn: 'root',

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

@ -1,10 +1,11 @@
import { State, Selector, createSelector, Action, StateContext, Store } from '@ngxs/store';
import { Config, ABP } from '../models';
import { Config } from '../models/config';
import { ABP } from '../models/common';
import { GetAppConfiguration, PatchRouteByName } from '../actions/config.actions';
import { ApplicationConfigurationService } from '../services/application-configuration.service';
import { tap, switchMap } from 'rxjs/operators';
import snq from 'snq';
import { SetLanguage } from '../actions';
import { SetLanguage } from '../actions/session.actions';
import { SessionState } from './session.state';
import { of } from 'rxjs';
import { setChildRoute, sortRoutes, organizeRoutes } from '../utils/route-utils';
@ -27,7 +28,7 @@ export class ConfigState {
static getOne(key: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
return state[key];
},
);
@ -46,7 +47,7 @@ export class ConfigState {
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
return (keys as string[]).reduce((acc, val) => {
if (acc) {
return acc[val];
@ -63,7 +64,7 @@ export class ConfigState {
static getRoute(path?: string, name?: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
const { flattedRoutes } = state;
return (flattedRoutes as ABP.FullRoute[]).find(route => {
if (path && route.path === path) {
@ -81,7 +82,7 @@ export class ConfigState {
static getApiUrl(key?: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State): string {
(state: Config.State): string => {
return state.environment.apis[key || 'default'].url;
},
);
@ -92,7 +93,7 @@ export class ConfigState {
static getSetting(key: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
return snq(() => state.setting.values[key]);
},
);
@ -103,7 +104,7 @@ export class ConfigState {
static getGrantedPolicy(key: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State): boolean {
(state: Config.State): boolean => {
if (!key) return true;
return snq(() => state.auth.grantedPolicies[key], false);
},
@ -112,20 +113,24 @@ export class ConfigState {
return selector;
}
/**
*
* @deprecated, Use getLocalization instead. To be delete in v1
*/
static getCopy(key: string, ...interpolateParams: string[]) {
if (!key) key = '';
const keys = key.split('::') as string[];
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
if (!state.localization) return key;
const { defaultResourceName } = state.environment.localization;
if (keys[0] === '') {
if (!defaultResourceName) {
throw new Error(
`Please check your environment. May you forget set defaultResourceName?
`Please check your environment. May you forget set defaultResourceName?
Here is the example:
{ production: false,
localization: {
@ -160,6 +165,61 @@ export class ConfigState {
return selector;
}
static getLocalization(key: string | Config.LocalizationWithDefault, ...interpolateParams: string[]) {
let defaultValue: string;
if (typeof key !== 'string') {
defaultValue = key.defaultValue;
key = key.key;
}
if (!key) key = '';
const keys = key.split('::') as string[];
const selector = createSelector(
[ConfigState],
(state: Config.State) => {
if (!state.localization) return defaultValue || key;
const { defaultResourceName } = state.environment.localization;
if (keys[0] === '') {
if (!defaultResourceName) {
throw new Error(
`Please check your environment. May you forget set defaultResourceName?
Here is the example:
{ production: false,
localization: {
defaultResourceName: 'MyProjectName'
}
}`,
);
}
keys[0] = snq(() => defaultResourceName);
}
let localization = (keys as any).reduce((acc, val) => {
if (acc) {
return acc[val];
}
return undefined;
}, state.localization.values);
interpolateParams = interpolateParams.filter(params => params != null);
if (localization && interpolateParams && interpolateParams.length) {
interpolateParams.forEach(param => {
localization = localization.replace(/[\'\"]?\{[\d]+\}[\'\"]?/, param);
});
}
return localization || defaultValue || key;
},
);
return selector;
}
constructor(private appConfigurationService: ApplicationConfigurationService, private store: Store) {}
@Action(GetAppConfiguration)

5
npm/ng-packs/packages/core/src/lib/states/session.state.ts

@ -4,6 +4,7 @@ import { ABP, Session } from '../models';
import { GetAppConfiguration } from '../actions/config.actions';
import { LocalizationService } from '../services/localization.service';
import { from, combineLatest } from 'rxjs';
import { switchMap } from 'rxjs/operators';
@State<Session.State>({
name: 'SessionState',
@ -28,7 +29,9 @@ export class SessionState {
language: payload,
});
return combineLatest([dispatch(new GetAppConfiguration()), from(this.localizationService.registerLocale(payload))]);
return dispatch(new GetAppConfiguration()).pipe(
switchMap(() => from(this.localizationService.registerLocale(payload))),
);
}
@Action(SetTenant)

343
npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts

@ -0,0 +1,343 @@
import { RouterTestingModule } from '@angular/router/testing';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { NGXS_PLUGINS, NgxsModule, InitState, Store } from '@ngxs/store';
import { environment } from '../../../../../apps/dev-app/src/environments/environment';
import { LAYOUTS } from '../../../../theme-basic/src/public-api';
import { ABP } from '../models';
import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS, ABP_ROUTES } from '../plugins';
import { RouterOutletComponent } from '../components';
import { ConfigState } from '../states';
import { CoreModule } from '../core.module';
import { eLayoutType } from '../enums/common';
ABP_ROUTES.push(
{
name: 'AbpUiNavigation::Menu:Administration',
path: '',
order: 1,
wrapper: true,
},
{
name: 'AbpIdentity::Menu:IdentityManagement',
path: 'identity',
order: 1,
parentName: 'AbpUiNavigation::Menu:Administration',
layout: eLayoutType.application,
iconClass: 'fa fa-id-card-o',
children: [
{ path: 'roles', name: 'AbpIdentity::Roles', order: 2, requiredPolicy: 'AbpIdentity.Roles' },
{ path: 'users', name: 'AbpIdentity::Users', order: 1, requiredPolicy: 'AbpIdentity.Users' },
],
},
{
name: 'AbpAccount::Menu:Account',
path: 'account',
invisible: true,
layout: eLayoutType.application,
children: [
{ path: 'login', name: 'AbpAccount::Login', order: 1 },
{ path: 'register', name: 'AbpAccount::Register', order: 2 },
],
},
{
name: 'AbpTenantManagement::Menu:TenantManagement',
path: 'tenant-management',
parentName: 'AbpUiNavigation::Menu:Administration',
layout: eLayoutType.application,
iconClass: 'fa fa-users',
children: [
{
path: 'tenants',
name: 'AbpTenantManagement::Tenants',
order: 1,
requiredPolicy: 'AbpTenantManagement.Tenants',
},
],
},
);
const expectedState = {
environment,
requirements: {
layouts: LAYOUTS,
},
routes: [
{
name: '::Menu:Home',
path: '',
children: [],
url: '/',
},
{
name: 'AbpAccount::Menu:Account',
path: 'account',
invisible: true,
layout: 'application',
children: [
{
path: 'login',
name: 'AbpAccount::Login',
order: 1,
url: '/account/login',
},
{
path: 'register',
name: 'AbpAccount::Register',
order: 2,
url: '/account/register',
},
],
url: '/account',
},
{
name: 'AbpUiNavigation::Menu:Administration',
path: '',
order: 1,
wrapper: true,
children: [
{
name: 'AbpIdentity::Menu:IdentityManagement',
path: 'identity',
order: 1,
parentName: 'AbpUiNavigation::Menu:Administration',
layout: 'application',
iconClass: 'fa fa-id-card-o',
children: [
{
path: 'users',
name: 'AbpIdentity::Users',
order: 1,
requiredPolicy: 'AbpIdentity.Users',
url: '/identity/users',
},
{
path: 'roles',
name: 'AbpIdentity::Roles',
order: 2,
requiredPolicy: 'AbpIdentity.Roles',
url: '/identity/roles',
},
],
url: '/identity',
},
{
name: 'AbpTenantManagement::Menu:TenantManagement',
path: 'tenant-management',
parentName: 'AbpUiNavigation::Menu:Administration',
layout: 'application',
iconClass: 'fa fa-users',
children: [
{
path: 'tenants',
name: 'AbpTenantManagement::Tenants',
order: 1,
requiredPolicy: 'AbpTenantManagement.Tenants',
url: '/tenant-management/tenants',
},
],
url: '/tenant-management',
},
],
},
],
flattedRoutes: [
{
name: '::Menu:Home',
path: '',
children: [],
url: '/',
},
{
name: 'AbpAccount::Menu:Account',
path: 'account',
invisible: true,
layout: 'application',
children: [
{
path: 'login',
name: 'AbpAccount::Login',
order: 1,
url: '/account/login',
},
{
path: 'register',
name: 'AbpAccount::Register',
order: 2,
url: '/account/register',
},
],
url: '/account',
},
{
path: 'login',
name: 'AbpAccount::Login',
order: 1,
url: '/account/login',
},
{
path: 'register',
name: 'AbpAccount::Register',
order: 2,
url: '/account/register',
},
{
name: 'AbpUiNavigation::Menu:Administration',
path: '',
order: 1,
wrapper: true,
children: [
{
name: 'AbpIdentity::Menu:IdentityManagement',
path: 'identity',
order: 1,
parentName: 'AbpUiNavigation::Menu:Administration',
layout: 'application',
iconClass: 'fa fa-id-card-o',
children: [
{
path: 'users',
name: 'AbpIdentity::Users',
order: 1,
requiredPolicy: 'AbpIdentity.Users',
url: '/identity/users',
},
{
path: 'roles',
name: 'AbpIdentity::Roles',
order: 2,
requiredPolicy: 'AbpIdentity.Roles',
url: '/identity/roles',
},
],
url: '/identity',
},
{
name: 'AbpTenantManagement::Menu:TenantManagement',
path: 'tenant-management',
parentName: 'AbpUiNavigation::Menu:Administration',
layout: 'application',
iconClass: 'fa fa-users',
children: [
{
path: 'tenants',
name: 'AbpTenantManagement::Tenants',
order: 1,
requiredPolicy: 'AbpTenantManagement.Tenants',
url: '/tenant-management/tenants',
},
],
url: '/tenant-management',
},
],
},
{
name: 'AbpIdentity::Menu:IdentityManagement',
path: 'identity',
order: 1,
parentName: 'AbpUiNavigation::Menu:Administration',
layout: 'application',
iconClass: 'fa fa-id-card-o',
children: [
{
path: 'users',
name: 'AbpIdentity::Users',
order: 1,
requiredPolicy: 'AbpIdentity.Users',
url: '/identity/users',
},
{
path: 'roles',
name: 'AbpIdentity::Roles',
order: 2,
requiredPolicy: 'AbpIdentity.Roles',
url: '/identity/roles',
},
],
url: '/identity',
},
{
path: 'users',
name: 'AbpIdentity::Users',
order: 1,
requiredPolicy: 'AbpIdentity.Users',
url: '/identity/users',
},
{
path: 'roles',
name: 'AbpIdentity::Roles',
order: 2,
requiredPolicy: 'AbpIdentity.Roles',
url: '/identity/roles',
},
{
name: 'AbpTenantManagement::Menu:TenantManagement',
path: 'tenant-management',
parentName: 'AbpUiNavigation::Menu:Administration',
layout: 'application',
iconClass: 'fa fa-users',
children: [
{
path: 'tenants',
name: 'AbpTenantManagement::Tenants',
order: 1,
requiredPolicy: 'AbpTenantManagement.Tenants',
url: '/tenant-management/tenants',
},
],
url: '/tenant-management',
},
{
path: 'tenants',
name: 'AbpTenantManagement::Tenants',
order: 1,
requiredPolicy: 'AbpTenantManagement.Tenants',
url: '/tenant-management/tenants',
},
],
};
describe('ConfigPlugin', () => {
let spectator: SpectatorService<ConfigPlugin>;
const createService = createServiceFactory({
service: ConfigPlugin,
imports: [
CoreModule,
NgxsModule.forRoot([]),
RouterTestingModule.withRoutes([
{
path: '',
component: RouterOutletComponent,
data: {
routes: {
name: '::Menu:Home',
} as ABP.Route,
},
},
{ path: 'identity', component: RouterOutletComponent },
{ path: 'account', component: RouterOutletComponent },
{ path: 'tenant-management', component: RouterOutletComponent },
]),
],
providers: [
{
provide: NGXS_PLUGINS,
useClass: ConfigPlugin,
multi: true,
},
{
provide: NGXS_CONFIG_PLUGIN_OPTIONS,
useValue: { environment, requirements: { layouts: LAYOUTS } } as ABP.Root,
},
],
});
beforeEach(() => {
spectator = createService();
});
it('should ConfigState must be create with correct datas', () => {
const store = spectator.get(Store);
const state = store.selectSnapshot(ConfigState);
expect(state).toEqual(expectedState);
});
});

157
npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts

@ -0,0 +1,157 @@
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { createRoutingFactory, SpectatorRouting, SpyObject } from '@ngneat/spectator';
import { NgxsModule, Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { NgxsResetPluginModule, StateOverwrite } from 'ngxs-reset-plugin';
import { LAYOUTS, ThemeBasicModule } from '../../../../theme-basic/src/public-api';
import { eLayoutType } from '../enums';
import { ABP } from '../models';
import { RouterOutletComponent, CoreModule, DynamicLayoutComponent, ConfigState } from '@abp/ng.core';
import { ThemeSharedModule } from '../../../../theme-shared/src/public-api';
import { MessageService } from 'primeng/components/common/messageservice';
@Component({
selector: 'abp-dummy',
template: '{{route.snapshot.data?.name}} works!',
})
class DummyComponent {
constructor(public route: ActivatedRoute) {}
}
describe('DynamicLayoutComponent', () => {
const createComponent = createRoutingFactory({
component: RouterOutletComponent,
declareComponent: false,
imports: [
CoreModule,
NgxsModule.forRoot([ConfigState]),
NgxsResetPluginModule.forRoot(),
ThemeSharedModule,
ThemeBasicModule,
],
declarations: [DummyComponent],
entryComponents: [],
stubsEnabled: false,
providers: [MessageService, { provide: OAuthService, useValue: { getAccessToken: () => true } }],
routes: [
{ path: '', component: RouterOutletComponent },
{
path: 'parentWithLayout',
component: DynamicLayoutComponent,
children: [
{
path: 'childWithoutLayout',
component: DummyComponent,
data: { name: 'childWithoutLayout' },
},
{
path: 'childWithLayout',
component: DummyComponent,
data: { name: 'childWithLayout' },
},
],
},
{
path: 'withData',
component: DynamicLayoutComponent,
children: [
{
path: '',
component: DummyComponent,
data: { name: 'withData' },
},
],
data: { layout: eLayoutType.empty },
},
{
path: 'withoutLayout',
component: DynamicLayoutComponent,
children: [
{
path: '',
component: DummyComponent,
data: { name: 'withoutLayout' },
},
],
data: { layout: null },
},
],
});
let spectator: SpectatorRouting<RouterOutletComponent>;
let store: SpyObject<Store>;
beforeEach(async () => {
spectator = createComponent();
store = spectator.get(Store);
store.dispatch(
new StateOverwrite([
ConfigState,
{
requirements: { layouts: LAYOUTS },
routes: [
{
path: '',
wrapper: true,
children: [
{
path: 'parentWithLayout',
layout: eLayoutType.application,
children: [{ path: 'childWithoutLayout' }, { path: 'childWithLayout', layout: eLayoutType.account }],
},
],
},
{ path: 'withData', layout: eLayoutType.application },
,
] as ABP.FullRoute[],
environment: { application: {} },
},
]),
);
});
it('should handle application layout from parent abp route and display it', async () => {
spectator.router.navigateByUrl('/parentWithLayout/childWithoutLayout');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-dynamic-layout')).toBeTruthy();
expect(spectator.query('abp-layout-application')).toBeTruthy();
});
it('should handle account layout from own property and display it', async () => {
spectator.router.navigateByUrl('/parentWithLayout/childWithLayout');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-layout-account')).toBeTruthy();
});
it('should handle empty layout from route data and display it', async () => {
spectator.router.navigateByUrl('/withData');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-layout-empty')).toBeTruthy();
});
it('should display empty layout when layout is null', async () => {
spectator.router.navigateByUrl('/withoutLayout');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-layout-empty')).toBeTruthy();
});
it('should not display any layout when layouts are empty', async () => {
store.dispatch(
new StateOverwrite([ConfigState, { ...store.selectSnapshot(ConfigState), requirements: { layouts: [] } }]),
);
spectator.detectChanges();
spectator.router.navigateByUrl('/withoutLayout');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-layout-empty')).toBeFalsy();
expect(spectator.query('abp-dynamic-layout').children[0].tagName).toEqual('ROUTER-OUTLET');
});
});

37
npm/ng-packs/packages/core/src/lib/tests/locale.provider.spec.ts

@ -0,0 +1,37 @@
import { Component, LOCALE_ID } from '@angular/core';
import { createRoutingFactory, SpectatorHost, SpectatorRouting } from '@ngneat/spectator';
import { LocalizationService } from '../services';
import { LocaleProvider, LocaleId } from '../providers';
import localesMapping from '../constants/different-locales';
@Component({ selector: 'abp-dummy', template: '' })
export class DummyComponent {}
describe('LocaleProvider', () => {
let spectator: SpectatorRouting<DummyComponent>;
const createComponent = createRoutingFactory({
component: DummyComponent,
stubsEnabled: false,
providers: [
{ provide: LocalizationService, useValue: { currentLang: 'en-US' } },
{
provide: LOCALE_ID,
useClass: LocaleId,
deps: [LocalizationService],
},
],
});
describe('#LOCALE_ID', () => {
it('should equal to localizationService.currentLang', async () => {
spectator = createComponent();
const localizationService = spectator.get(LocalizationService);
expect(spectator.get(LOCALE_ID).valueOf()).toBe(localesMapping['en-US'] || 'en-US');
localizationService.currentLang = 'tr';
expect(spectator.get(LOCALE_ID).valueOf()).toBe(localesMapping['tr'] || 'tr');
});
});
});

81
npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts

@ -0,0 +1,81 @@
import { Router } from '@angular/router';
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator';
import { Store } from '@ngxs/store';
import { Observable, of } from 'rxjs';
import { LocalizationService } from '../services/localization.service';
describe('LocalizationService', () => {
let spectator: SpectatorService<LocalizationService>;
let store: SpyObject<Store>;
let service: LocalizationService;
const createService = createServiceFactory({
service: LocalizationService,
entryComponents: [],
mocks: [Store, Router],
});
beforeEach(() => {
spectator = createService();
store = spectator.get(Store);
service = spectator.service;
});
describe('#currentLang', () => {
it('should be tr', () => {
store.selectSnapshot.andCallFake((selector: (state: any, ...states: any[]) => string) => {
return selector({ SessionState: { getLanguage: 'tr' } });
});
expect(service.currentLang).toBe('tr');
});
});
describe('#get', () => {
it('should be return an observable localization', async () => {
store.select.andCallFake((selector: (state: any, ...states: any[]) => Observable<string>) => {
return selector({ ConfigState: { getLocalization: (keys, ...interpolateParams) => of(keys) } });
});
const localization = await service.get('AbpTest').toPromise();
expect(localization).toBe('AbpTest');
});
});
describe('#instant', () => {
it('should be return a localization', () => {
store.selectSnapshot.andCallFake((selector: (state: any, ...states: any[]) => Observable<string>) => {
return selector({ ConfigState: { getLocalization: (keys, ...interpolateParams) => keys } });
});
expect(service.instant('AbpTest')).toBe('AbpTest');
});
});
describe('#registerLocale', () => {
it('should return registerLocale and then call setRouteReuse', () => {
const router = spectator.get(Router);
const shouldReuseRoute = () => true;
router.routeReuseStrategy = { shouldReuseRoute } as any;
router.navigateByUrl.andCallFake(url => {
return new Promise(resolve => resolve({ catch: () => null }));
});
service.registerLocale('tr');
expect(router.navigated).toBe(false);
expect(router.routeReuseStrategy.shouldReuseRoute).not.toEqual(shouldReuseRoute);
});
it('should throw an error message when service have an otherInstance', async () => {
try {
const instance = new LocalizationService(null, null, null, {} as any);
} catch (error) {
expect((error as Error).message).toBe('LocaleService should have only one instance.');
}
});
});
});

15
npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts

@ -0,0 +1,15 @@
import { Spectator, createComponentFactory, createHostFactory } from '@ngneat/spectator';
import { RouterTestingModule } from '@angular/router/testing';
import { RouterOutletComponent } from '../components/router-outlet.component';
describe('RouterOutletComponent', () => {
let spectator: Spectator<RouterOutletComponent>;
const createHost = createHostFactory({ component: RouterOutletComponent, imports: [RouterTestingModule] });
it('should have a router-outlet element', () => {
spectator = createHost('<abp-router-outlet></abp-router-outlet>');
console.log((spectator.debugElement.nativeElement as HTMLElement).children);
expect((spectator.debugElement.nativeElement as HTMLElement).children.length).toBe(1);
expect((spectator.debugElement.nativeElement as HTMLElement).children[0].tagName).toBe('ROUTER-OUTLET');
});
});

45
npm/ng-packs/packages/core/src/lib/tests/state-overwrite.ts

@ -0,0 +1,45 @@
import { Inject, Injectable, InjectionToken, Provider } from '@angular/core';
import { getActionTypeFromInstance, InitState, NgxsPlugin, NGXS_PLUGINS, setValue } from '@ngxs/store';
export const NGXS_OVERWRITE_PLUGIN_VALUE = new InjectionToken('NGXS_OVERWRITE_PLUGIN_VALUE');
export class StateOverwrite {
static readonly type = '[StateOverwrite] Patch';
constructor(public payload: { stateName: string; value: any }) {}
}
@Injectable()
export class OverwritePlugin implements NgxsPlugin {
initialized: boolean;
constructor(@Inject(NGXS_OVERWRITE_PLUGIN_VALUE) private options: any) {}
handle(state, action, next) {
const type = getActionTypeFromInstance(action);
if (action instanceof InitState && !this.initialized) {
state = { ...state, ...this.options };
this.initialized = true;
}
if (type === StateOverwrite.type) {
state = setValue(state, action.payload.stateName, action.payload.value);
}
return next(state, action);
}
}
export function stateOverwriteProviders(value = {}): Provider[] {
return [
{
provide: NGXS_PLUGINS,
useClass: OverwritePlugin,
multi: true,
},
{
provide: NGXS_OVERWRITE_PLUGIN_VALUE,
useValue: value,
},
];
}

1
npm/ng-packs/packages/core/src/lib/utils/common-utils.ts

@ -1,4 +1,5 @@
export function noop() {
// tslint:disable-next-line: only-arrow-functions
const fn = function() {};
return fn;
}

3
npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts

@ -1,5 +1,6 @@
export function uuid(a?: any): string {
return a
? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16)
? // tslint:disable-next-line: no-bitwise
(a ^ ((Math.random() * 16) >> (a / 4))).toString(16)
: ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid);
}

7
npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts

@ -3,10 +3,9 @@ import { Injector } from '@angular/core';
import { Store } from '@ngxs/store';
import { GetAppConfiguration } from '../actions/config.actions';
import differentLocales from '../constants/different-locales';
import { SessionState } from '../states/session.state';
export function getInitialData(injector: Injector) {
const fn = function() {
const fn = () => {
const store: Store = injector.get(Store);
return store.dispatch(new GetAppConfiguration()).toPromise();
@ -16,10 +15,10 @@ export function getInitialData(injector: Injector) {
}
export function localeInitializer(injector: Injector) {
const fn = function() {
const fn = () => {
const store: Store = injector.get(Store);
const lang = store.selectSnapshot(SessionState.getLanguage) || 'en';
const lang = store.selectSnapshot(state => state.SessionState.language) || 'en';
return new Promise((resolve, reject) => {
registerLocale(lang).then(() => resolve(), reject);

5
npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts

@ -6,18 +6,19 @@ function isFunction(value) {
}
export const takeUntilDestroy = (componentInstance, destroyMethodName = 'ngOnDestroy') => <T>(
source: Observable<T>,
source: Observable<T>
) => {
const originalDestroy = componentInstance[destroyMethodName];
if (isFunction(originalDestroy) === false) {
throw new Error(
`${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}`,
`${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}`
);
}
if (!componentInstance['__takeUntilDestroy']) {
componentInstance['__takeUntilDestroy'] = new Subject();
componentInstance[destroyMethodName] = function() {
// tslint:disable-next-line: no-unused-expression
isFunction(originalDestroy) && originalDestroy.apply(this, arguments);
componentInstance['__takeUntilDestroy'].next(true);
componentInstance['__takeUntilDestroy'].complete();

18
npm/ng-packs/packages/core/tsconfig.spec.json

@ -2,16 +2,12 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
"types": ["jasmine", "node"],
"paths": {
"@abp/ng.core": ["packages/core/src/public-api.ts"],
"@abp/ng.core/*": ["packages/core/src/lib/*"]
}
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
"files": ["src/test.ts"],
"include": ["**/*.spec.ts", "**/*.d.ts"]
}

20
npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts

@ -9,7 +9,7 @@ import { pluck, tap } from 'rxjs/operators';
@Component({
selector: 'abp-feature-management',
templateUrl: './feature-management.component.html',
templateUrl: './feature-management.component.html'
})
export class FeatureManagementComponent {
@Input()
@ -32,13 +32,12 @@ export class FeatureManagementComponent {
if (value) this.openModal();
}
@Output()
visibleChange = new EventEmitter<boolean>();
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(FeatureManagementState.getFeatures)
features$: Observable<FeatureManagement.Feature[]>;
modalBusy: boolean = false;
modalBusy = false;
form: FormGroup;
@ -54,7 +53,12 @@ export class FeatureManagementComponent {
getFeatures() {
this.store
.dispatch(new GetFeatures({ providerKey: this.providerKey, providerName: this.providerName }))
.dispatch(
new GetFeatures({
providerKey: this.providerKey,
providerName: this.providerName
})
)
.pipe(pluck('FeatureManagementState', 'features'))
.subscribe(features => {
this.buildForm(features);
@ -78,7 +82,7 @@ export class FeatureManagementComponent {
features = features.map((feature, i) => ({
name: feature.name,
value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i],
value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i]
}));
this.store
@ -86,8 +90,8 @@ export class FeatureManagementComponent {
new UpdateFeatures({
providerKey: this.providerKey,
providerName: this.providerName,
features,
}),
features
})
)
.subscribe(() => {
this.modalBusy = false;

2
npm/ng-packs/packages/feature-management/src/lib/states/index.ts

@ -1 +1 @@
export * from "./feature-management.state";
export * from './feature-management.state';

1
npm/ng-packs/packages/identity-config/README.md

@ -0,0 +1 @@
# @abp/ng.identity.config

32
npm/ng-packs/packages/identity-config/karma.conf.js

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/identity-config'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

7
npm/ng-packs/packages/identity-config/ng-package.json

@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/identity-config",
"lib": {
"entryFile": "src/public-api.ts"
}
}

7
npm/ng-packs/packages/identity-config/package.json

@ -0,0 +1,7 @@
{
"name": "@abp/ng.identity.config",
"version": "0.0.1",
"publishConfig": {
"access": "public"
}
}

8
npm/ng-packs/packages/identity-config/src/lib/identity-config.module.ts

@ -0,0 +1,8 @@
import { noop } from '@abp/ng.core';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { IdentityConfigService } from './services/identity-config.service';
@NgModule({
providers: [{ provide: APP_INITIALIZER, deps: [IdentityConfigService], useFactory: noop, multi: true }],
})
export class IdentityConfigModule {}

32
npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts

@ -0,0 +1,32 @@
import { ABP_ROUTES, eLayoutType, RestService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class IdentityConfigService {
constructor(private router: Router, private restService: RestService) {
ABP_ROUTES.push(
{
name: 'AbpUiNavigation::Menu:Administration',
path: '',
order: 1,
wrapper: true,
},
{
name: 'AbpIdentity::Menu:IdentityManagement',
path: 'identity',
order: 1,
parentName: 'AbpUiNavigation::Menu:Administration',
layout: eLayoutType.application,
iconClass: 'fa fa-id-card-o',
children: [
{ path: 'roles', name: 'AbpIdentity::Roles', order: 2, requiredPolicy: 'AbpIdentity.Roles' },
{ path: 'users', name: 'AbpIdentity::Users', order: 1, requiredPolicy: 'AbpIdentity.Users' },
],
},
);
}
}

2
npm/ng-packs/packages/identity-config/src/public-api.ts

@ -0,0 +1,2 @@
export * from './lib/services/identity-config.service';
export * from './lib/identity-config.module';

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

Loading…
Cancel
Save