Browse Source

Merge branch 'dev' into auto-merge/rel-7-1/1758

pull/15803/head
Engincan VESKE 3 years ago
parent
commit
7ac5bc2eda
  1. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  3. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  4. 2
      common.props
  5. 10
      docs/en/Dependency-Injection.md
  6. 230
      docs/en/Migration-Guides/IdentityServer4-Step-by-Step.md
  7. 6
      docs/en/Modules/IdentityServer.md
  8. 2
      docs/en/UI/Angular/Quick-Start.md
  9. 6
      docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
  10. 21
      docs/en/UI/Blazor/Basic-Theme.md
  11. 2
      docs/en/UI/Blazor/Overall.md
  12. 2
      docs/en/UI/Blazor/Theming.md
  13. 20
      docs/en/docs-nav.json
  14. 10
      docs/zh-Hans/Dependency-Injection.md
  15. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/FlagIconCss/FlagIconCssStyleContributor.cs
  16. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs
  17. 16
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  18. 2
      framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs
  19. 2
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
  20. 4
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs
  21. 2
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs
  22. 22
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs
  23. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/IAuthService.cs
  24. 30
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs
  25. 94
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SuiteAppSettingsService.cs
  26. 65
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs
  27. 4
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs
  28. 2
      framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs
  29. 2
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
  30. 4
      framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs
  31. 2
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs
  32. 2
      framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs
  33. 2
      framework/src/Volo.Abp.Security/Volo/Abp/Security/AbpSecurityModule.cs
  34. 2
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs
  35. 2
      framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/AbpTextTemplatingCoreModule.cs
  36. 2
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/AbpUnitOfWorkModule.cs
  37. 4
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs
  38. 4
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs
  39. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json
  40. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json
  41. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/de.json
  42. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/el.json
  43. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en-GB.json
  44. 5
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json
  45. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json
  46. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fa.json
  47. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fi.json
  48. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fr.json
  49. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hi.json
  50. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json
  51. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json
  52. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/is.json
  53. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/it.json
  54. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/nl.json
  55. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json
  56. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json
  57. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ro-RO.json
  58. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json
  59. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sk.json
  60. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sl.json
  61. 5
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json
  62. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json
  63. 5
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json
  64. 3
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json
  65. 2
      framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs
  66. 2
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs
  67. 2
      framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs
  68. 2
      framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs
  69. 30
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json
  70. 9
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs
  71. 2
      modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor
  72. 4
      modules/blogging/src/Volo.Blogging.Admin.Web/Navigation/BloggingAdminMenuContributor.cs
  73. 8
      modules/blogging/src/Volo.Blogging.Admin.Web/Navigation/BloggingAdminMenuNames.cs
  74. 6
      modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Index.cshtml
  75. 4
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  76. 15
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaException.cs
  77. 13
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/SimpleMathsCaptchaGenerator.cs
  78. 53
      npm/ng-packs/angular.json
  79. 6
      npm/ng-packs/apps/dev-app/src/app/app.module.ts
  80. 18
      npm/ng-packs/apps/dev-app/src/app/home/home.component.html
  81. 9
      npm/ng-packs/package.json
  82. 20
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.html
  83. 38
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts
  84. 30
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts
  85. 9
      npm/ng-packs/packages/theme-shared/src/lib/components/card/card-body.component.ts
  86. 9
      npm/ng-packs/packages/theme-shared/src/lib/components/card/card-title.component.ts
  87. 22
      npm/ng-packs/packages/theme-shared/src/lib/components/card/card.component.ts
  88. 14
      npm/ng-packs/packages/theme-shared/src/lib/components/card/card.module.ts
  89. 4
      npm/ng-packs/packages/theme-shared/src/lib/components/card/index.ts
  90. 45
      npm/ng-packs/packages/theme-shared/src/lib/components/checkbox/checkbox.component.ts
  91. 46
      npm/ng-packs/packages/theme-shared/src/lib/components/form-input/form-input.component.ts
  92. 1
      npm/ng-packs/packages/theme-shared/src/lib/components/index.ts
  93. 2
      npm/ng-packs/packages/theme-shared/src/lib/directives/visible.directive.ts
  94. 4
      npm/ng-packs/packages/theme-shared/src/lib/enums/form.ts
  95. 1
      npm/ng-packs/packages/theme-shared/src/lib/enums/index.ts
  96. 44
      npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts
  97. 46
      npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts
  98. 17
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
  99. 4
      npm/ng-packs/tsconfig.base.json
  100. 6
      npm/packs/flag-icon-css/abp.resourcemapping.js

2
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -421,7 +421,7 @@
"ContentCacheSlidingExpirationByDay": "Content Cache Sliding Expiration By Day",
"MaxDaysForCaching": "Max Days For Caching",
"Enabled": "Enabled",
"Menu:NugetPackagesContentCache": "NuGet Packages Content Cache",
"Menu:NugetPackagesContentCache": "NuGet Cache",
"NugetPackagesContentCache": "NuGet Content Cache",
"SlidingExpritionByDayInfo": "Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. This will not extend the entry lifetime beyond the absolute expiration.",
"MaxDaysForCachingInfo": "Gets or sets an absolute expiration time, relative to now.",

1
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json

@ -27,6 +27,7 @@
"Volo.AbpIo.Domain:030010": "To purchase the trial license, you first need to activate your trial license!",
"Volo.AbpIo.Domain:030011": "You cannot delete a trial license when it is purchased!",
"Volo.AbpIo.Domain:030012": "A user is entitled to have only 1 free trial period. You already used your trial license.",
"Volo.AbpIo.Domain:030013": "A user with an active license cannot start a trial license.",
"Volo.AbpIo.Domain:070000": "The organization name can only contain latin letters, numbers, dots and hyphens!",
"Volo.AbpIo.Domain:070001": "The company name can only contain latin letters, numbers, dots, space and hyphens!",
"WantToLearn?": "Want to learn?",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json

@ -196,7 +196,7 @@
"ChangingDevelopers": "Can I change the registered developers of my organization in the future?",
"ChangingDevelopersExplanation": "In addition to adding new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.",
"WhatHappensWhenLicenseEnds": "What happens when my license period ends?",
"WhatHappensWhenLicenseEndsExplanation1": "The ABP Commercial license is a <a href=\"{0}\" target=\"_blank\">perpetual license</a>. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. In order to continue to get new features, performance enhancements, bug fixes, support and continue using ABP Suite, you need to renew your license. When your license expires, you will not get the following benefits:",
"WhatHappensWhenLicenseEndsExplanation1": "The ABP Commercial license is a <a href=\"{0}\" target=\"_blank\">perpetual license</a>. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. In order to continue to get new features, performance enhancements, bug fixes, support and continue using ABP Suite, you need to renew your license. When your license expires;",
"WhatHappensWhenLicenseEndsExplanation2": "You can not create new solutions using the ABP Commercial, but you can continue developing your existing applications forever.",
"WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MINOR version (except RC or Preview versions). For example: if you are using v3.2.0 of a module, you can still get updates for v3.2.x (v3.2.1, v3.2.5... etc.) of that module. But you cannot get updates for the next major or minor version (like v3.3.0, v3.3.3, 4.x.x.. etc.). For example, when your license expired, the latest release was v4.4.3, and later, it published both 4.4.4 version and 4.5.0 version, you would be able to access the v4.4.X but you wouldn't be access the v4.5.X.",
"WhatHappensWhenLicenseEndsExplanation4": "You can not install new modules and themes added to the ABP Commercial platform after your license ends.",

2
common.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>7.1.0-rc.2</Version>
<Version>7.2.0</Version>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>

10
docs/en/Dependency-Injection.md

@ -440,16 +440,16 @@ Use `ICachedServiceProvider` (instead of `ITransientCachedServiceProvider`) unle
## Advanced Features
### IServiceCollection.OnRegistred Event
### IServiceCollection.OnRegistered Event
You may want to perform an action for every service registered to the dependency injection. In the `PreConfigureServices` method of your module, register a callback using the `OnRegistred` method as shown below:
You may want to perform an action for every service registered to the dependency injection. In the `PreConfigureServices` method of your module, register a callback using the `OnRegistered` method as shown below:
````csharp
public class AppModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(ctx =>
context.Services.OnRegistered(ctx =>
{
var type = ctx.ImplementationType;
//...
@ -465,7 +465,7 @@ public class AppModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(ctx =>
context.Services.OnRegistered(ctx =>
{
if (ctx.ImplementationType.IsDefined(typeof(MyLogAttribute), true))
{
@ -478,7 +478,7 @@ public class AppModule : AbpModule
This example simply checks if the service class has `MyLogAttribute` attribute and adds `MyLogInterceptor` to the interceptor list if so.
> Notice that `OnRegistred` callback might be called multiple times for the same service class if it exposes more than one service/interface. So, it's safe to use `Interceptors.TryAdd` method instead of `Interceptors.Add` method. See [the documentation](Dynamic-Proxying-Interceptors.md) of dynamic proxying / interceptors.
> Notice that `OnRegistered` callback might be called multiple times for the same service class if it exposes more than one service/interface. So, it's safe to use `Interceptors.TryAdd` method instead of `Interceptors.Add` method. See [the documentation](Dynamic-Proxying-Interceptors.md) of dynamic proxying / interceptors.
## 3rd-Party Providers

230
docs/en/Migration-Guides/IdentityServer4-Step-by-Step.md

@ -0,0 +1,230 @@
# Migrating from OpenIddict to IdentityServer4 Step by Step Guide
ABP startup templates use `OpenIddict` OpenID provider from v6.0.0 by default and `IdentityServer` projects are renamed to `AuthServer` in tiered/separated solutions. Since OpenIddict is the default OpenID provider library for ABP templates since v6.0, you may want to keep using [IdentityServer4](https://github.com/IdentityServer/IdentityServer4) library, even it is **archived and no longer maintained by the owners**. ABP doesn't provide support for newer versions of IdentityServer. This guide provides layer-by-layer guidance for migrating your existing [OpenIddict](https://github.com/openiddict/openiddict-core) application to IdentityServer4.
## IdentityServer4 Migration Steps
Use the `abp update` command to update your existing application. See [Upgrading docs](../Upgrading.md) for more info. Apply required migrations by following the [Migration Guides](Index.md) based on your application version.
### Domain.Shared Layer
- In **MyApplication.Domain.Shared.csproj** replace **project reference**:
```csharp
<PackageReference Include="Volo.Abp.OpenIddict.Domain.Shared" Version="6.0.*" />
```
with
```csharp
<PackageReference Include="Volo.Abp.IdentityServer.Domain.Shared" Version="6.0.*" />
```
- In **MyApplicationDomainSharedModule.cs** replace usings and **module dependencies:**
```csharp
using Volo.Abp.OpenIddict;
...
typeof(AbpOpenIddictDomainSharedModule)
```
with
```csharp
using Volo.Abp.IdentityServer;
...
typeof(AbpIdentityServerDomainSharedModule)
```
### Domain Layer
- In **MyApplication.Domain.csproj** replace **project references**:
```csharp
<PackageReference Include="Volo.Abp.OpenIddict.Domain" Version="6.0.*" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.OpenIddict" Version="6.0.*" />
```
with
```csharp
<PackageReference Include="Volo.Abp.IdentityServer.Domain" Version="6.0.*" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.IdentityServer" Version="6.0.*" />
```
- In **MyApplicationDomainModule.cs** replace usings and **module dependencies**:
```csharp
using Volo.Abp.OpenIddict;
using Volo.Abp.PermissionManagement.OpenIddict;
...
typeof(AbpOpenIddictDomainModule),
typeof(AbpPermissionManagementDomainOpenIddictModule),
```
with
```csharp
using Volo.Abp.IdentityServer;
using Volo.Abp.PermissionManagement.IdentityServer;
...
typeof(AbpIdentityServerDomainModule),
typeof(AbpPermissionManagementDomainIdentityServerModule),
```
#### OpenIddictDataSeedContributor
DataSeeder is the most important part for starting the application since it seeds the initial data for both OpenID providers.
- Create a folder named *IdentityServer* under the Domain project and copy the [IdentityServerDataSeedContributor.cs](https://github.com/abpframework/abp-samples/blob/master/Ids2OpenId/src/Ids2OpenId.Domain/IdentityServer/IdentityServerDataSeedContributor.cs) under this folder. **Rename** all the `OpenId2Ids` with your project name.
- Delete *OpenIddict* folder that contains `OpenIddictDataSeedContributor.cs` which is no longer needed.
### EntityFrameworkCore Layer
If you are using MongoDB, skip this step and check the *MongoDB* layer section.
- In **MyApplication.EntityFrameworkCore.csproj** replace **project reference**:
```csharp
<PackageReference Include="Volo.Abp.OpenIddict.EntityFrameworkCore" Version="6.0.*" />
```
with
```csharp
<PackageReference Include="Volo.Abp.IdentityServer.EntityFrameworkCore" Version="6.0.*" />
```
- In **MyApplicationEntityFrameworkCoreModule.cs** replace usings and **module dependencies**:
```csharp
using Volo.Abp.OpenIddict.EntityFrameworkCore;
...
typeof(AbpOpenIddictEntityFrameworkCoreModule),
```
with
```csharp
using Volo.Abp.IdentityServer.EntityFrameworkCore;
...
typeof(AbpIdentityServerEntityFrameworkCoreModule),
```
- In **MyApplicationDbContext.cs** replace usings and **fluent api configurations**:
```csharp
using Volo.Abp.OpenIddict.EntityFrameworkCore;
...
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
...
builder.ConfigureOpenIddict();
```
with
```csharp
using Volo.Abp.IdentityServer.EntityFrameworkCore;
...
using Volo.Abp.OpenIddict.EntityFrameworkCore;
...
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
...
builder.ConfigureIdentityServer();
```
> Not: You need to create new migration after updating the fluent api. Navigate to *EntityFrameworkCore* folder and add a new migration. Ex, `dotnet ef migrations add Updated_To_IdentityServer `
### MongoDB Layer
If you are using EntityFrameworkCore, skip this step and check the *EntityFrameworkCore* layer section.
- In **MyApplication.MongoDB.csproj** replace **project reference**:
```csharp
<PackageReference Include="Volo.Abp.OpenIddict.MongoDB" Version="6.0.*" />
```
with
```csharp
<PackageReference Include="Volo.Abp.IdentityServer.MongoDB" Version="6.0.*" />
```
- In **MyApplicationMongoDbModule.cs** replace usings and **module dependencies**:
```csharp
using Volo.Abp.OpenIddict.MongoDB;
...
typeof(AbpOpenIddictMongoDbModule),
```
with
```csharp
using Volo.Abp.IdentityServer.MongoDB;
...
typeof(AbpIdentityServerMongoDbModule),
```
### DbMigrator Project
- In `appsettings.json` **replace OpenIddict section with IdentityServer** since IdentityServerDataSeeder will be using these information for initial data seeding:
```json
"IdentityServer": { // Rename OpenIddict to IdentityServer
"Clients ": { // Rename Applications to Clients
...
}
}
```
### Test Project
- In **MyApplicationTestBaseModule.cs** **add** the IdentityServer related using and PreConfigurations:
```csharp
using Volo.Abp.IdentityServer;
```
and
```csharp
PreConfigure<AbpIdentityServerBuilderOptions>(options =>
{
options.AddDeveloperSigningCredential = false;
});
PreConfigure<IIdentityServerBuilder>(identityServerBuilder =>
{
identityServerBuilder.AddDeveloperSigningCredential(false, System.Guid.NewGuid().ToString());
});
```
to `PreConfigureServices` to run authentication related unit tests.
### UI Layer
You can follow the migrations guides from IdentityServer to OpenIddict in **reverse order** to update your UIs. You can also check the source-code for [Index.cshtml.cs](https://github.com/abpframework/abp-samples/blob/master/OpenId2Ids/src/OpenId2Ids.AuthServer/Pages/Index.cshtml) and [Index.cshtml](https://github.com/abpframework/abp-samples/blob/master/OpenId2Ids/src/OpenId2Ids.AuthServer/Pages/Index.cshtml.cs) files for **AuthServer** project.
- [Angular UI Migration](OpenIddict-Angular.md)
- [MVC/Razor UI Migration](OpenIddict-Mvc.md)
- [Blazor-Server UI Migration](OpenIddict-Blazor-Server.md)
- [Blazor-Wasm UI Migration](OpenIddict-Blazor.md)
## Source code of samples and module
* [Open source tiered & separate auth server application migrate OpenIddict to Identity Server](https://github.com/abpframework/abp-samples/tree/master/OpenId2Ids)
* [IdentityServer module document](https://docs.abp.io/en/abp/6.0/Modules/IdentityServer)
* [IdentityServer module source code](https://github.com/abpframework/abp/tree/rel-6.0/modules/identityserver)

6
docs/en/Modules/IdentityServer.md

@ -1,10 +1,12 @@
# IdentityServer Module
IdentityServer module provides a full integration with the [IdentityServer](https://github.com/IdentityServer/IdentityServer4) (IDS) framework, which provides advanced authentication features like single sign-on and API access control. This module persists clients, resources and other IDS-related objects to database.
IdentityServer module provides a full integration with the [IdentityServer4](https://github.com/IdentityServer/IdentityServer4) (IDS) framework, which provides advanced authentication features like single sign-on and API access control. This module persists clients, resources and other IDS-related objects to database. **This module is replaced by** [OpenIddict module](https://docs.abp.io/en/abp/latest/Modules/OpenIddict) after ABP v6.0 in the startup templates.
> Note: You can not use IdentityServer and OpenIddict modules together. They are separate OpenID provider libraries for the same job.
## How to Install
This module comes as pre-installed (as NuGet/NPM packages). You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
You don't need this module when you are using OpenIddict module. However, if you want to keep using IdentityServer4 for your applications, you can install this module and remove the OpenIddict module. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
### The Source Code

2
docs/en/UI/Angular/Quick-Start.md

@ -179,7 +179,7 @@ This command will download and start a simple static server, a browser window at
Of course, you need your application to run on an optimized web server and become available to everyone. This is quite straight-forward:
1. Create a new static web server instance. You can use a service like [Azure App Service](https://azure.microsoft.com/tr-tr/services/app-service/web/), [Firebase](https://firebase.google.com/docs/hosting), [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/), or even [GitHub Pages](https://angular.io/guide/deployment#deploy-to-github-pages). Another option is maintaining own web server with [NGINX](https://www.nginx.com/), [IIS](https://www.iis.net/), [Apache HTTP Server](https://httpd.apache.org/), or equivalent.
1. Create a new static web server instance. You can use a service like [Azure App Service](https://azure.microsoft.com/en-us/services/app-service/web/), [Firebase](https://firebase.google.com/docs/hosting), [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/), or even [GitHub Pages](https://angular.io/guide/deployment#deploy-to-github-pages). Another option is maintaining own web server with [NGINX](https://www.nginx.com/), [IIS](https://www.iis.net/), [Apache HTTP Server](https://httpd.apache.org/), or equivalent.
2. Copy the files from `dist/MyProjectName` <sup id="a-dist-folder-name">[1](#f-dist-folder-name)</sup> to a publicly served destination on the server via CLI of the service provider, SSH, or FTP (whichever is available). This step would be defined as a job if you have a CI/CD flow.
3. [Configure the server](https://angular.io/guide/deployment#server-configuration) to redirect all requests to the _index.html_ file. Some services do that automatically. Others require you [to add a file to the bundle via assets](https://angular.io/guide/workspace-config#assets-configuration) which describes the server how to do the redirections. Occasionally, you may need to do manual configuration.

6
docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md

@ -10,7 +10,7 @@ See the [form elements demo page](https://bootstrap-taghelpers.abp.io/Components
## abp-input
`abp-input` tag creates a Bootstrap form input for a given c# property. It uses [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-input-tag-helper) in background, so every data annotation attribute of `input` tag helper of Asp.Net Core is also valid for `abp-input`.
`abp-input` tag creates a Bootstrap form input for a given c# property. It uses [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) in background, so every data annotation attribute of `input` tag helper of Asp.Net Core is also valid for `abp-input`.
Usage:
@ -88,7 +88,7 @@ You can set some of the attributes on your c# property, or directly on html tag.
* `label`: Sets the label for input.
* `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`.
`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-input-tag-helper) are also valid for `abp-input` tag helper.
`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) are also valid for `abp-input` tag helper.
### Label & Localization
@ -100,7 +100,7 @@ You can set label of your input in different ways:
## abp-select
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [Asp.Net Core Select Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of Asp.Net Core is also valid for `abp-select`.
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [Asp.Net Core Select Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of Asp.Net Core is also valid for `abp-select`.
`abp-select` tag needs a list of `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem ` to work. It can be provided by `asp-items` attriube on the tag or `[SelectItems()]` attribute on c# property. (if you are using [abp-dynamic-form](Dynamic-forms.md), c# attribute is the only way.)

21
docs/en/UI/Blazor/Basic-Theme.md

@ -85,6 +85,27 @@ You can simply override the styles in the Global Styles file of your application
See the [Customization / Overriding Components](Customization-Overriding-Components.md) to learn how you can replace components, customize and extend the user interface.
### Overriding the Menu Item
Basic theme supports overriding a single menu item with a custom component. You can create a custom component and call `UseComponent` extension method of Basic Theme in the **MenuContributor**.
```csharp
using Volo.Abp.AspNetCore.Components.Web.BasicTheme.Navigation;
//...
context.Menu.Items.Add(
new ApplicationMenuItem("Custom.1", "My Custom Menu", "#")
.UseComponent(typeof(MyMenuItemComponent)));
```
```html
<li class="nav-item">
<a href="#" class="nav-link">
My Custom Menu
</a>
</li>
```
### Copy & Customize
You can run the following [ABP CLI](../../CLI.md) command in **Blazor{{if UI == "Blazor"}}WebAssembly{{else}} Server{{end}}** project directory to copy the source code to your solution:

2
docs/en/UI/Blazor/Overall.md

@ -88,7 +88,7 @@ There are a set of standard libraries that comes pre-installed and supported by
* [Twitter Bootstrap](https://getbootstrap.com/) as the fundamental HTML/CSS framework.
* [Blazorise](https://github.com/stsrki/Blazorise) as a component library that supports the Bootstrap and adds extra components like Data Grid and Tree.
* [FontAwesome](https://fontawesome.com/) as the fundamental CSS font library.
* [Flag Icon](https://github.com/lipis/flag-icon-css) as a library to show flags of countries.
* [Flag Icon](https://github.com/lipis/flag-icons) as a library to show flags of countries.
These libraries are selected as the base libraries and available to the applications and modules.

2
docs/en/UI/Blazor/Theming.md

@ -48,7 +48,7 @@ All the themes must depend on the [Volo.Abp.AspNetCore.Components.Server.Theming
* [Twitter Bootstrap](https://getbootstrap.com/) as the fundamental HTML/CSS framework.
* [Blazorise](https://github.com/stsrki/Blazorise) as a component library that supports the Bootstrap and adds extra components like Data Grid and Tree.
* [FontAwesome](https://fontawesome.com/) as the fundamental CSS font library.
* [Flag Icon](https://github.com/lipis/flag-icon-css) as a library to show flags of countries.
* [Flag Icon](https://github.com/lipis/flag-icons) as a library to show flags of countries.
These libraries are selected as the base libraries and available to the applications and modules.

20
docs/en/docs-nav.json

@ -1370,16 +1370,22 @@
},
{
"text": "IdentityServer",
"path": "Modules/IdentityServer.md"
"path": "Modules/IdentityServer.md",
"items": [
{
"text": "IdentityServer Migration Guide",
"path": "Migration-Guides/IdentityServer4-Step-by-Step.md"
}
]
},
{
"text": "OpenIddict",
"items": [
{
"text": "OpenIddict Migration Guide",
"path": "Migration-Guides/OpenIddict-Step-by-Step.md"
}
],
"items": [
{
"text": "OpenIddict Migration Guide",
"path": "Migration-Guides/OpenIddict-Step-by-Step.md"
}
],
"path": "Modules/OpenIddict.md"
},
{

10
docs/zh-Hans/Dependency-Injection.md

@ -270,16 +270,16 @@ using (var scope = _serviceProvider.CreateScope())
## 高级特性
### IServiceCollection.OnRegistred 事件
### IServiceCollection.OnRegistered 事件
你可能想在注册到依赖注入的每个服务上执行一个操作, 在你的模块的 `PreConfigureServices` 方法中, 使用 `OnRegistred` 方法注册一个回调(callback) , 如下所示:
你可能想在注册到依赖注入的每个服务上执行一个操作, 在你的模块的 `PreConfigureServices` 方法中, 使用 `OnRegistered` 方法注册一个回调(callback) , 如下所示:
````csharp
public class AppModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(ctx =>
context.Services.OnRegistered(ctx =>
{
var type = ctx.ImplementationType;
//...
@ -295,7 +295,7 @@ public class AppModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(ctx =>
context.Services.OnRegistered(ctx =>
{
if (ctx.ImplementationType.IsDefined(typeof(MyLogAttribute), true))
{
@ -308,7 +308,7 @@ public class AppModule : AbpModule
这个示例判断一个服务类是否具有 `MyLogAttribute` 特性, 如果有的话就添加一个 `MyLogInterceptor` 到拦截器集合中.
> 注意, 如果服务类公开了多于一个服务或接口, `OnRegistred` 回调(callback)可能被同一服务类多次调用. 因此, 较安全的方法是使用 `Interceptors.TryAdd` 方法而不是 `Interceptors.Add` 方法. 请参阅动态代理(dynamic proxying)/拦截器 [文档](Dynamic-Proxying-Interceptors.md).
> 注意, 如果服务类公开了多于一个服务或接口, `OnRegistered` 回调(callback)可能被同一服务类多次调用. 因此, 较安全的方法是使用 `Interceptors.TryAdd` 方法而不是 `Interceptors.Add` 方法. 请参阅动态代理(dynamic proxying)/拦截器 [文档](Dynamic-Proxying-Interceptors.md).
## 第三方提供程序

9
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/FlagIconCss/FlagIconCssStyleContributor.cs

@ -7,6 +7,13 @@ public class FlagIconCssStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/flag-icon-css/css/flag-icons.min.css");
if (context.FileProvider.GetFileInfo("/libs/flag-icons/css/flag-icons.min.css").Exists)
{
context.Files.AddIfNotContains("/libs/flag-icons/css/flag-icons.min.css");
}
else if (context.FileProvider.GetFileInfo("/libs/flag-icon-css/css/flag-icons.min.css").Exists)
{
context.Files.AddIfNotContains("/libs/flag-icon-css/css/flag-icons.min.css");
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs

@ -39,7 +39,7 @@ public class AbpAspNetCoreMvcUiWidgetsModule : AbpModule
{
var widgetTypes = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (WidgetAttribute.IsWidget(context.ImplementationType))
{

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

@ -43,6 +43,7 @@ using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.UI;
using Volo.Abp.UI.Navigation;
using Volo.Abp.Validation.Localization;
namespace Volo.Abp.AspNetCore.Mvc;
@ -174,10 +175,17 @@ public class AbpAspNetCoreMvcModule : AbpModule
context.Services.Replace(ServiceDescriptor.Singleton<IValidationAttributeAdapterProvider, AbpValidationAttributeAdapterProvider>());
context.Services.AddSingleton<ValidationAttributeAdapterProvider>();
Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(context.Services);
});
context.Services.AddOptions<MvcOptions>()
.Configure<IServiceProvider>((mvcOptions, serviceProvider) =>
{
mvcOptions.AddAbp(context.Services);
// serviceProvider is root service provider.
var stringLocalizer = serviceProvider.GetRequiredService<IStringLocalizer<AbpValidationResource>>();
mvcOptions.ModelBindingMessageProvider.SetValueIsInvalidAccessor(_ => stringLocalizer["The value '{0}' is invalid."]);
mvcOptions.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(() => stringLocalizer["The field must be a number."]);
mvcOptions.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(value => stringLocalizer["The field {0} must be a number.", value]);
});
Configure<AbpEndpointRouterOptions>(options =>
{

2
framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs

@ -91,7 +91,7 @@ public class AbpAspNetCoreSignalRModule : AbpModule
{
var hubTypes = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (IsHubClass(context) && !IsDisabledForAutoMap(context))
{

2
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs

@ -24,7 +24,7 @@ public class AbpAuditingModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
context.Services.OnRegistered(AuditingInterceptorRegistrar.RegisterIfNeeded);
}
public override void ConfigureServices(ServiceConfigurationContext context)

4
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs

@ -22,7 +22,7 @@ public class AbpAuthorizationModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded);
context.Services.OnRegistered(AuthorizationInterceptorRegistrar.RegisterIfNeeded);
AutoAddDefinitionProviders(context.Services);
}
@ -64,7 +64,7 @@ public class AbpAuthorizationModule : AbpModule
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (typeof(IPermissionDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{

2
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs

@ -21,7 +21,7 @@ public class AbpBackgroundJobsAbstractionsModule : AbpModule
{
var jobTypes = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IBackgroundJob<>)) ||
ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IAsyncBackgroundJob<>)))

22
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs

@ -58,7 +58,7 @@ public class AuthService : IAuthService, ITransientDependency
{
if (!response.IsSuccessStatusCode)
{
Logger.LogError("Remote server returns '{response.StatusCode}'");
Logger.LogError($"Remote server returns '{response.StatusCode}'");
return null;
}
@ -127,6 +127,26 @@ public class AuthService : IAuthService, ITransientDependency
}
}
public async Task<bool> CheckMultipleOrganizationsAsync(string username)
{
var url = $"{CliUrls.WwwAbpIo}api/license/check-multiple-organizations?username={username}";
var client = CliHttpClientFactory.CreateClient();
using (var response = await client.GetHttpResponseMessageWithRetryAsync(url, CancellationTokenProvider.Token, Logger))
{
if (!response.IsSuccessStatusCode)
{
throw new Exception($"ERROR: Remote server returns '{response.StatusCode}'");
}
await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(response);
var responseContent = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<bool>(responseContent);
}
}
private async Task LogoutAsync(string accessToken)
{
try

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/IAuthService.cs

@ -9,4 +9,6 @@ public interface IAuthService
Task LoginAsync(string userName, string password, string organizationName = null);
Task LogoutAsync();
Task<bool> CheckMultipleOrganizationsAsync(string username);
}

30
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs

@ -2,13 +2,11 @@
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Auth;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
@ -26,17 +24,13 @@ public class LoginCommand : IConsoleCommand, ITransientDependency
public ICancellationTokenProvider CancellationTokenProvider { get; }
public IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; }
private readonly CliHttpClientFactory _cliHttpClientFactory;
public LoginCommand(AuthService authService,
ICancellationTokenProvider cancellationTokenProvider,
IRemoteServiceExceptionHandler remoteServiceExceptionHandler,
CliHttpClientFactory cliHttpClientFactory)
IRemoteServiceExceptionHandler remoteServiceExceptionHandler)
{
AuthService = authService;
CancellationTokenProvider = cancellationTokenProvider;
RemoteServiceExceptionHandler = remoteServiceExceptionHandler;
_cliHttpClientFactory = cliHttpClientFactory;
Logger = NullLogger<LoginCommand>.Instance;
}
@ -111,7 +105,7 @@ public class LoginCommand : IConsoleCommand, ITransientDependency
private async Task<bool> HasMultipleOrganizationAndThisNotSpecified(CommandLineArgs commandLineArgs, string organization)
{
if (string.IsNullOrWhiteSpace(organization) &&
await CheckMultipleOrganizationsAsync(commandLineArgs.Target))
await AuthService.CheckMultipleOrganizationsAsync(commandLineArgs.Target))
{
Logger.LogError($"You have multiple organizations, please specify your organization with `--organization` parameter.");
return true;
@ -168,26 +162,6 @@ public class LoginCommand : IConsoleCommand, ITransientDependency
return false;
}
private async Task<bool> CheckMultipleOrganizationsAsync(string username)
{
var url = $"{CliUrls.WwwAbpIo}api/license/check-multiple-organizations?username={username}";
var client = _cliHttpClientFactory.CreateClient();
using (var response = await client.GetHttpResponseMessageWithRetryAsync(url, CancellationTokenProvider.Token, Logger))
{
if (!response.IsSuccessStatusCode)
{
throw new Exception($"ERROR: Remote server returns '{response.StatusCode}'");
}
await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(response);
var responseContent = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<bool>(responseContent);
}
}
public string GetUsageInfo()
{
var sb = new StringBuilder();

94
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SuiteAppSettingsService.cs

@ -0,0 +1,94 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands.Services;
public class SuiteAppSettingsService : ITransientDependency
{
private const int DefaultPort = 3000;
public CmdHelper CmdHelper { get; }
public SuiteAppSettingsService(CmdHelper cmdHelper)
{
CmdHelper = cmdHelper;
}
public async Task<int> GetSuitePortAsync()
{
return await GetSuitePortAsync(GetCurrentSuiteVersion());
}
public async Task<int> GetSuitePortAsync(string version)
{
var filePath = GetFilePathOrNull(version);
if (filePath == null)
{
return DefaultPort;
}
var content = File.ReadAllText(filePath);
var contentAsJson = JObject.Parse(content);
var url = contentAsJson["AbpSuite"]?["ApplicationUrl"]?.ToString();
if (url == null)
{
return DefaultPort;
}
return Convert.ToInt32(url.Split(":").Last());
}
private string GetFilePathOrNull(string version)
{
if (version == null)
{
return null;
}
var path = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
".dotnet",
"tools",
".store",
"volo.abp.suite",
version,
"volo.abp.suite",
version,
"tools",
"net7.0",
"any",
"appsettings.json"
);
if (!File.Exists(path))
{
return null;
}
return path;
}
private string GetCurrentSuiteVersion()
{
var dotnetToolList = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g", out int exitCode);
var suiteLine = dotnetToolList.Split(Environment.NewLine)
.FirstOrDefault(l => l.ToLower().StartsWith("volo.abp.suite "));
if (string.IsNullOrEmpty(suiteLine))
{
return null;
}
return suiteLine.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1];
}
}

65
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs

@ -35,23 +35,26 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
private readonly PackageVersionCheckerService _packageVersionCheckerService;
private readonly AuthService _authService;
private readonly CliHttpClientFactory _cliHttpClientFactory;
private readonly SuiteAppSettingsService _suiteAppSettingsService;
private const string SuitePackageName = "Volo.Abp.Suite";
public ILogger<SuiteCommand> Logger { get; set; }
private const string AbpSuiteHost = "http://localhost:3000";
private int _abpSuitePort = 3000;
public SuiteCommand(
AbpNuGetIndexUrlService nuGetIndexUrlService,
PackageVersionCheckerService packageVersionCheckerService,
ICmdHelper cmdHelper,
AuthService authService,
CliHttpClientFactory cliHttpClientFactory)
CliHttpClientFactory cliHttpClientFactory,
SuiteAppSettingsService suiteAppSettingsService)
{
CmdHelper = cmdHelper;
_nuGetIndexUrlService = nuGetIndexUrlService;
_packageVersionCheckerService = packageVersionCheckerService;
_authService = authService;
_cliHttpClientFactory = cliHttpClientFactory;
_suiteAppSettingsService = suiteAppSettingsService;
Logger = NullLogger<SuiteCommand>.Instance;
}
@ -72,17 +75,20 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
commandLineArgs.Options.ContainsKey(Options.Preview.Long);
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
var currentSuiteVersionAsString = GetCurrentSuiteVersion();
switch (operationType)
{
case "":
case null:
await InstallSuiteIfNotInstalledAsync();
await InstallSuiteIfNotInstalledAsync(currentSuiteVersionAsString);
_abpSuitePort = await _suiteAppSettingsService.GetSuitePortAsync(currentSuiteVersionAsString);
RunSuite();
break;
case "generate":
await InstallSuiteIfNotInstalledAsync();
await InstallSuiteIfNotInstalledAsync(currentSuiteVersionAsString);
_abpSuitePort = await _suiteAppSettingsService.GetSuitePortAsync(currentSuiteVersionAsString);
var suiteProcess = StartSuite();
System.Threading.Thread.Sleep(500); //wait for initialization of the app
await GenerateCrudPageAsync(commandLineArgs);
@ -130,7 +136,7 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
}
var IsSolutionBuiltResponse = await client.GetAsync(
$"{AbpSuiteHost}/api/abpSuite/solutions/{solutionId.ToString()}/is-built"
$"http://localhost:{_abpSuitePort}/api/abpSuite/solutions/{solutionId.ToString()}/is-built"
);
var IsSolutionBuilt = Convert.ToBoolean(await IsSolutionBuiltResponse.Content.ReadAsStringAsync());
@ -148,7 +154,7 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
);
var responseMessage = await client.PostAsync(
$"{AbpSuiteHost}/api/abpSuite/crudPageGenerator/{solutionId.ToString()}/save-and-generate-entity",
$"http://localhost:{_abpSuitePort}/api/abpSuite/crudPageGenerator/{solutionId.ToString()}/save-and-generate-entity",
entityContent
);
@ -178,7 +184,7 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
}
var responseMessage = await client.GetHttpResponseMessageWithRetryAsync(
"http://localhost:3000/api/abpSuite/solutions",
$"http://localhost:{_abpSuitePort}/api/abpSuite/solutions",
_cliHttpClientFactory.GetCancellationToken(TimeSpan.FromMinutes(10)),
Logger,
timeIntervals.ToArray());
@ -216,7 +222,7 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
);
var responseMessage = await client.PostAsync(
"http://localhost:3000/api/abpSuite/addSolution",
$"http://localhost:{_abpSuitePort}/api/abpSuite/addSolution",
entityContent,
_cliHttpClientFactory.GetCancellationToken(TimeSpan.FromMinutes(10))
);
@ -234,11 +240,9 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
}
}
private async Task InstallSuiteIfNotInstalledAsync()
private async Task InstallSuiteIfNotInstalledAsync(string currentSuiteVersion)
{
var currentSuiteVersionAsString = GetCurrentSuiteVersion();
if (string.IsNullOrEmpty(currentSuiteVersionAsString))
if (string.IsNullOrEmpty(currentSuiteVersion))
{
await InstallSuiteAsync();
}
@ -430,6 +434,19 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
Logger.LogWarning("Couldn't check ABP Suite installed status: " + ex.Message);
}
if (IsSuiteAlreadyRunning())
{
Logger.LogInformation("Opening suite...");
CmdHelper.Open($"http://localhost:{_abpSuitePort}");
return;
}
if (IsPortAlreadyInUse())
{
Logger.LogError($"Port \"{_abpSuitePort}\" is already in use.");
return;
}
CmdHelper.RunCmd("abp-suite");
}
@ -453,23 +470,32 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
return null;
}
if (IsPortAlreadyInUse())
{
Logger.LogError($"Port \"{_abpSuitePort}\" is already in use.");
return null;
}
return CmdHelper.RunCmdAndGetProcess("abp-suite --no-browser");
}
private bool IsSuiteAlreadyRunning()
{
return GetProcessesRelatedWithSuite().Any();
}
private bool IsPortAlreadyInUse()
{
var ipGP = IPGlobalProperties.GetIPGlobalProperties();
var endpoints = ipGP.GetActiveTcpListeners();
return endpoints.Any(e => e.Port == 3000);
return endpoints.Any(e => e.Port == _abpSuitePort);
}
private void KillSuite()
{
try
{
var suiteProcesses = (from p in Process.GetProcesses()
where p.ProcessName.ToLower().Contains("abp-suite")
select p);
var suiteProcesses = GetProcessesRelatedWithSuite();
foreach (var suiteProcess in suiteProcesses)
{
@ -483,6 +509,13 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
}
}
private IEnumerable<Process> GetProcessesRelatedWithSuite()
{
return (from p in Process.GetProcesses()
where p.ProcessName.ToLower().Contains("abp-suite")
select p);
}
public string GetUsageInfo()
{
var sb = new StringBuilder();

4
framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs

@ -5,9 +5,9 @@ namespace Microsoft.Extensions.DependencyInjection;
public static class ServiceCollectionRegistrationActionExtensions
{
// OnRegistred
// OnRegistered
public static void OnRegistred(this IServiceCollection services, Action<IOnServiceRegistredContext> registrationAction)
public static void OnRegistered(this IServiceCollection services, Action<IOnServiceRegistredContext> registrationAction)
{
GetOrCreateRegistrationActionList(services).Add(registrationAction);
}

2
framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs

@ -42,7 +42,7 @@ public class AbpDataModule : AbpModule
{
var contributors = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (typeof(IDataSeedContributor).IsAssignableFrom(context.ImplementationType))
{

2
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs

@ -47,7 +47,7 @@ public class AbpEventBusModule : AbpModule
var localHandlers = new List<Type>();
var distributedHandlers = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(ILocalEventHandler<>)))
{

4
framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs

@ -22,7 +22,7 @@ public class AbpFeaturesModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(FeatureInterceptorRegistrar.RegisterIfNeeded);
context.Services.OnRegistered(FeatureInterceptorRegistrar.RegisterIfNeeded);
AutoAddDefinitionProviders(context.Services);
}
@ -57,7 +57,7 @@ public class AbpFeaturesModule : AbpModule
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (typeof(IFeatureDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{

2
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs

@ -124,7 +124,7 @@ public class FeatureDefinition : ICanCreateChildFeature
}
/// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary.
/// Adds one or more providers to the <see cref="AllowedProviders"/> list.
/// This is a shortcut for nested calls on this object.
/// </summary>
public virtual FeatureDefinition WithProviders(params string[] providers)

2
framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs

@ -17,7 +17,7 @@ public class AbpGlobalFeaturesModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(GlobalFeatureInterceptorRegistrar.RegisterIfNeeded);
context.Services.OnRegistered(GlobalFeatureInterceptorRegistrar.RegisterIfNeeded);
}
public override void ConfigureServices(ServiceConfigurationContext context)

2
framework/src/Volo.Abp.Security/Volo/Abp/Security/AbpSecurityModule.cs

@ -63,7 +63,7 @@ public class AbpSecurityModule : AbpModule
{
var contributorTypes = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (typeof(IAbpClaimsPrincipalContributor).IsAssignableFrom(context.ImplementationType))
{

2
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs

@ -36,7 +36,7 @@ public class AbpSettingsModule : AbpModule
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (typeof(ISettingDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{

2
framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/AbpTextTemplatingCoreModule.cs

@ -23,7 +23,7 @@ public class AbpTextTemplatingCoreModule : AbpModule
var definitionProviders = new List<Type>();
var contentContributors = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (typeof(ITemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{

2
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/AbpUnitOfWorkModule.cs

@ -7,6 +7,6 @@ public class AbpUnitOfWorkModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(UnitOfWorkInterceptorRegistrar.RegisterIfNeeded);
context.Services.OnRegistered(UnitOfWorkInterceptorRegistrar.RegisterIfNeeded);
}
}

4
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs

@ -193,7 +193,7 @@ public class UnitOfWork : IUnitOfWork, ITransientDependency
if (_databaseApis.ContainsKey(key))
{
throw new AbpException("There is already a database API in this unit of work with given key: " + key);
throw new AbpException("There is already a database API in this unit of work with given key.");
}
_databaseApis.Add(key, api);
@ -221,7 +221,7 @@ public class UnitOfWork : IUnitOfWork, ITransientDependency
if (_transactionApis.ContainsKey(key))
{
throw new AbpException("There is already a transaction API in this unit of work with given key: " + key);
throw new AbpException("There is already a transaction API in this unit of work with given key.");
}
_transactionApis.Add(key, api);

4
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs

@ -16,7 +16,7 @@ public class AbpValidationModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(ValidationInterceptorRegistrar.RegisterIfNeeded);
context.Services.OnRegistered(ValidationInterceptorRegistrar.RegisterIfNeeded);
AutoAddObjectValidationContributors(context.Services);
}
@ -39,7 +39,7 @@ public class AbpValidationModule : AbpModule
{
var contributorTypes = new List<Type>();
services.OnRegistred(context =>
services.OnRegistered(context =>
{
if (typeof(IObjectValidationContributor).IsAssignableFrom(context.ImplementationType))
{

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "يجب أن يكون الحقل {0} سلسلة أحرف طولها {1} كحد أدنى و {2} كحد أقصى.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "الحقل {0} ليس عنوانا URL صالحًا مؤهلاً بالكامل سواء كان عنوان http أو https أو ftp",
"The field {0} is invalid.": "الحقل {0} غير صالح.",
"The value '{0}' is invalid.": "القيمة '{0}' غير صالحة.",
"The field {0} must be a number.": "يجب أن يكون الحقل {0} رقمًا.",
"The field must be a number.": "يجب أن يكون الحقل رقمًا.",
"ThisFieldIsNotAValidCreditCardNumber.": "هذا الحقل لا يمثل رقم بطاقة ائتمان صالح.",
"ThisFieldIsNotValid.": "هذا الحقل غير صالح.",
"ThisFieldIsNotAValidEmailAddress.": "هذا الحقل لا يمثل عنوان بريد إلكتروني صالح.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musí být řetězec o minimální délce {2} a maximální délce {1} znaků.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Pole {0} není platná plně kvalifikovaná adresa http, https, nebo ftp URL.",
"The field {0} is invalid.": "Pole {0} je neplatné.",
"The value '{0}' is invalid.": "Hodnota '{0}' je neplatná.",
"The field {0} must be a number.": "Pole {0} musí být číslo.",
"The field must be a number.": "Pole musí být číslo.",
"ThisFieldIsNotAValidCreditCardNumber.": "V poli {0} není platné číslo kreditní karty.",
"ThisFieldIsNotValid.": "{0} není platný.",
"ThisFieldIsNotAValidEmailAddress.": "V poli {0} není platný email.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/de.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Das Feld {0} muss eine Zeichenfolge mit einer Mindestlänge von {2} und einer Maximallänge von {1} sein.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Das Feld {0} ist keine gültige, vollqualifizierte http-, https- oder ftp-URL.",
"The field {0} is invalid.": "Das Feld {0} ist ungültig.",
"The value '{0}' is invalid.": "Der Wert '{0}' ist ungültig.",
"The field {0} must be a number.": "Das Feld {0} muss eine Zahl sein.",
"The field must be a number.": "Das Feld muss eine Zahl sein.",
"ThisFieldIsNotAValidCreditCardNumber.": "Dieses Feld ist keine gültige Kreditkartennummer.",
"ThisFieldIsNotValid.": "Dieses Feld ist ungültig.",
"ThisFieldIsNotAValidEmailAddress.": "Dieses Feld ist keine gültige E-Mail-Adresse.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/el.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Το πεδίο {0} πρέπει να είναι μια συμβολοσειρά με ελάχιστο μήκος {2} και μέγιστο μήκος {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Το πεδίο {0} δεν είναι έγκυρη πλήρως πιστοποιημένη διεύθυνση URL http, https ή ftp.",
"The field {0} is invalid.": "Το πεδίο {0} δεν είναι έγκυρο.",
"The value '{0}' is invalid.": "Η τιμή '{0}' δεν είναι έγκυρη.",
"The field {0} must be a number.": "Το πεδίο {0} πρέπει να είναι αριθμός.",
"The field must be a number.": "Το πεδίο πρέπει να είναι αριθμός.",
"ThisFieldIsNotAValidCreditCardNumber.": "Αυτό το πεδίο δεν περιέχει έγκυρο αριθμό πιστωτικής κάρτας.",
"ThisFieldIsNotValid.": "Αυτό το πεδίο δεν είναι έγκυρο.",
"ThisFieldIsNotAValidEmailAddress.": "Αυτό το πεδίο δεν περιέχει έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en-GB.json

@ -17,6 +17,9 @@
"The field {0} is invalid.": "The field {0} is invalid.",
"ThisFieldIsNotAValidCreditCardNumber.": "This field is not a valid credit card number.",
"ThisFieldIsNotValid.": "This field is not valid.",
"The value '{0}' is invalid.": "The value '{0}' is invalid.",
"The field {0} must be a number.": "The field {0} must be a number.",
"The field must be a number.": "The field must be a number.",
"ThisFieldIsNotAValidEmailAddress.": "This field is not a valid e-mail address.",
"ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "This field only accepts files with the following extensions: {0}",
"ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthOf{0}": "This field must be a string or array type with a maximum length of '{0}'.",

5
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "The {0} field is not a valid fully-qualified http, https, or ftp URL.",
"The field {0} is invalid.": "The field {0} is invalid.",
"The value '{0}' is invalid.": "The value '{0}' is invalid.",
"The field {0} must be a number.": "The field {0} must be a number.",
"The field must be a number.": "The field must be a number.",
"ThisFieldIsNotAValidCreditCardNumber.": "This field is not a valid credit card number.",
"ThisFieldIsNotValid.": "This field is not valid.",
"ThisFieldIsNotAValidEmailAddress.": "This field is not a valid e-mail address.",
@ -33,4 +36,4 @@
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "This field is not a valid fully-qualified http, https, or ftp URL.",
"ThisFieldIsInvalid.": "This field is invalid."
}
}
}

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "El campo {0} debe ser una cadena con una longitud mínima de {2} y máxima de {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "El campo {0} no es una URL (http, https o ftp) valida.",
"The field {0} is invalid.": "El campo {0} no es valido.",
"The value '{0}' is invalid.": "El valor '{0}' no es válido.",
"The field {0} must be a number.": "El campo {0} debe ser un número.",
"The field must be a number.": "El campo debe ser un número.",
"ThisFieldIsNotAValidCreditCardNumber.": "El campo no es un número de tarjeta de crédito valido.",
"ThisFieldIsNotValid.": "Este campo no es valido.",
"ThisFieldIsNotAValidEmailAddress.": "Este campo no es una dirección de e-mail valida.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fa.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "فیلد {0} باید رشته ای با حداقل طول {2} و حداکثر باشد طول {1}. ",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "فیلد {0} یک نشانی اینترنتی http ، https ، یا ftp کاملاً واجد شرایط نیست.",
"The field {0} is invalid.": "فیلد {0} نامعتبر است.",
"The value '{0}' is invalid.": "مقدار '{0}' نامعتبر است.",
"The field {0} must be a number.": "فیلد {0} باید یک عدد باشد.",
"The field must be a number.": "فیلد باید یک عدد باشد.",
"ThisFieldIsNotAValidCreditCardNumber.": "این قسمت شماره کارت اعتباری معتبری نیست.",
"ThisFieldIsNotValid.": "این قسمت معتبر نیست.",
"ThisFieldIsNotAValidEmailAddress.": "این قسمت آدرس ایمیل معتبری نیست.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fi.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Kentän {0} on oltava merkkijono, jonka vähimmäispituus on {2} ja enimmäispituus {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} -kenttä ei ole kelvollinen täysin hyväksytty http, https tai ftp URL.",
"The field {0} is invalid.": "Kenttä {0} on virheellinen.",
"The value '{0}' is invalid.": "Arvo '{0}' on virheellinen.",
"The field {0} must be a number.": "Kentän {0} on oltava numero.",
"The field must be a number.": "Kentän on oltava numero.",
"ThisFieldIsNotAValidCreditCardNumber.": "Tämä kenttä ei ole kelvollinen luottokortin numero.",
"ThisFieldIsNotValid.": "Tämä kenttä ei kelpaa.",
"ThisFieldIsNotAValidEmailAddress.": "Tämä kenttä ei ole kelvollinen sähköpostiosoite.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fr.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Le champ {0} doit être une chaîne d'une longueur minimale de {2} et d'une longueur maximale de {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Le champ {0} n'est pas une URL http, https ou ftp complète valide.",
"The field {0} is invalid.": "Le champ {0} n'est pas valide.",
"The value '{0}' is invalid.": "La valeur '{0}' n'est pas valide.",
"The field {0} must be a number.": "Le champ {0} doit être un nombre.",
"The field must be a number.": "Le champ doit être un nombre.",
"ThisFieldIsNotAValidCreditCardNumber.": "Ce champ n'est pas un numéro de carte de crédit valide.",
"ThisFieldIsNotValid.": "Ce champ n'est pas valide.",
"ThisFieldIsNotAValidEmailAddress.": "Ce champ n'est pas une adresse e-mail valide.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hi.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "फ़ील्ड {0} की लंबाई न्यूनतम {2} और अधिकतम लंबाई {1} होनी चाहिए।",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} फ़ील्ड मान्य पूर्णत: योग्य http, https, या ftp URL नहीं है।",
"The field {0} is invalid.": "फ़ील्ड {0} अमान्य है।",
"The value '{0}' is invalid.": "मान '{0}' अमान्य है।",
"The field {0} must be a number.": "फ़ील्ड {0} एक संख्या होनी चाहिए।",
"The field must be a number.": "फ़ील्ड एक संख्या होनी चाहिए।",
"ThisFieldIsNotAValidCreditCardNumber.": "यह फ़ील्ड मान्य क्रेडिट कार्ड नंबर नहीं है।",
"ThisFieldIsNotValid.": "यह फ़ील्ड मान्य नहीं है।",
"ThisFieldIsNotAValidEmailAddress.": "यह फ़ील्ड मान्य ई-मेल पता नहीं है।",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Polje {0} mora biti text sa minimalnom du�inom od {2} i maksimalnom dužinom od {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} nije valjan potpuno kvalificirani http, https, ili ftp URL.",
"The field {0} is invalid.": "Polje {0} nije važeće.",
"The value '{0}' is invalid.": "Vrijednost '{0}' nije važeća.",
"The field {0} must be a number.": "Polje {0} mora biti broj.",
"The field must be a number.": "Polje mora biti broj.",
"ThisFieldIsNotAValidCreditCardNumber.": "Ovo polje nije važeći broj kreditne kartice.",
"ThisFieldIsNotValid.": "Ovo polje nije valjano.",
"ThisFieldIsNotAValidEmailAddress.": "Ovo polje nije valjana e-mail adresa.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "A {0} mezőnek szöveget kell tartalmaznia minimum {2} és maximum {1} hosszan.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "A {0} mező nem érvényes fully-qualified http, https, vagy ftp URL cím.",
"The field {0} is invalid.": "A {0} mező nem érvényes.",
"The value '{0}' is invalid.": "A(z) '{0}' érték érvénytelen.",
"The field {0} must be a number.": "A(z) {0} mezőnek számnak kell lennie.",
"The field must be a number.": "A mezőnek számnak kell lennie.",
"ThisFieldIsNotAValidCreditCardNumber.": "A mező nem érvényes bankkártya számot tartalmaz.",
"ThisFieldIsNotValid.": "A mező nem érvényes.",
"ThisFieldIsNotAValidEmailAddress.": "A mező nem érvényes email cím.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/is.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Reiturinn {0} verður að vera strengur með lágmarkslengd {2} og hámarkslengd {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Reiturinn {0} er ekki fullgild http, https, eða ftp slóð.",
"The field {0} is invalid.": "Reiturinn {0} er ekki rétt útfylltur.",
"The value '{0}' is invalid.": "Gildið '{0}' er ógilt.",
"The field {0} must be a number.": "Reiturinn {0} verður að vera númer.",
"The field must be a number.": "Reiturinn verður að vera númer.",
"ThisFieldIsNotAValidCreditCardNumber.": "Þessi reitur hefur ekki gilt kreditkortanúmer.",
"ThisFieldIsNotValid.": "Reiturinn er ekki rétt útfylltur.",
"ThisFieldIsNotAValidEmailAddress.": "Þessi reitur hefur ekki gilt netfang.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/it.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Il campo {0} deve essere una stringa con una lunghezza minima di {2} e una lunghezza massima di {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Il campo {0} non è un URL http, https o ftp completo e valido.",
"The field {0} is invalid.": "Il campo {0} non è valido.",
"The value '{0}' is invalid.": "Il valore '{0}' non è valido.",
"The field {0} must be a number.": "Il campo {0} deve essere un numero.",
"The field must be a number.": "Il campo deve essere un numero.",
"ThisFieldIsNotAValidCreditCardNumber.": "Questo campo non è un numero di carta di credito valido.",
"ThisFieldIsNotValid.": "Questo campo non è valido.",
"ThisFieldIsNotAValidEmailAddress.": "Questo campo non è un indirizzo e-mail valido.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/nl.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Het veld {0} moet een tekenreeks zijn met een minimale lengte van {2} en een maximale lengte van {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Het veld {0} is geen geldige, volledig gekwalificeerde http-, https- of ftp-URL.",
"The field {0} is invalid.": "Het veld {0} is ongeldig.",
"The value '{0}' is invalid.": "De waarde '{0}' is ongeldig.",
"The field {0} must be a number.": "Het veld {0} moet een getal zijn.",
"The field must be a number.": "Het veld moet een getal zijn.",
"ThisFieldIsNotAValidCreditCardNumber.": "Dit veld is geen geldig krediet kaartnummer.",
"ThisFieldIsNotValid.": "Dir veld is ongeldig.",
"ThisFieldIsNotAValidEmailAddress.": "Dit veld is geen geldig e-mail adres.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musi być łańcuchem znaków o minimalnej długości {2} i maksymalnej długości {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Pole {0} nie jest prawidłowym, w pełni kwalifikowanym adresem URL http, https lub ftp.",
"The field {0} is invalid.": "Pole {0} jest niepoprawne.",
"The value '{0}' is invalid.": "Wartość '{0}' jest nieprawidłowa.",
"The field {0} must be a number.": "Pole {0} musi być liczbą.",
"The field must be a number.": "Pole musi być liczbą.",
"ThisFieldIsNotAValidCreditCardNumber.": "To pole nie jest prawidłowym numerem karty kredytowej.",
"ThisFieldIsNotValid.": "To pole jest nieprawidłowe.",
"ThisFieldIsNotAValidEmailAddress.": "To pole nie jest prawidłowym adresem e-mail.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "O campo {0} deve ser uma palavra com o tamanho mínimo de {2} e tamanho máximo de {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "O campo {0} não é um http, https, ou FTP válido.",
"The field {0} is invalid.": "O campo {0} é inválido.",
"The value '{0}' is invalid.": "O valor '{0}' é inválido.",
"The field {0} must be a number.": "O campo {0} deve ser um número.",
"The field must be a number.": "O campo deve ser um número.",
"ThisFieldIsNotAValidCreditCardNumber.": "Não é um cartão de crédito válido.",
"ThisFieldIsNotValid.": "Campo inválido.",
"ThisFieldIsNotAValidEmailAddress.": "E-mail inválido.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ro-RO.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Câmpul {0} trebuie să fie un string cu lungimea minimă de {2} şi lungimea maximă de {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Câmpul {0} nu este o adresă validă complet http, https sau ftp.",
"The field {0} is invalid.": "Câmpul {0} este invalid.",
"The value '{0}' is invalid.": "Valoarea '{0}' este nevalidă.",
"The field {0} must be a number.": "Câmpul {0} trebuie să fie un număr.",
"The field must be a number.": "Câmpul trebuie să fie un număr.",
"ThisFieldIsNotAValidCreditCardNumber.": "Acest câmp nu este un număr de card de credit valid.",
"ThisFieldIsNotValid.": "Acest câmp nu este valid.",
"ThisFieldIsNotAValidEmailAddress.": "Acest câmp nu este o adresă de e-mail validă.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Поле {0} должно быть строкой с минимальной длиной {2} и максимальной длиной {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Поле {0} не является действительным полным http, https или ftp адресом.",
"The field {0} is invalid.": "Значение в поле {0} недопустимо.",
"The value '{0}' is invalid.": "Значение '{0}' недопустимо.",
"The field {0} must be a number.": "Поле {0} должно быть числом.",
"The field must be a number.": "Поле должно быть числом.",
"ThisFieldIsNotAValidCreditCardNumber.": "Это поле не содержит действительный номер кредитной карты.",
"ThisFieldIsNotValid.": "Значение в этом поле недействительно.",
"ThisFieldIsNotAValidEmailAddress.": "Это поле не содержит действительный адрес электронной почты.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sk.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musí byť reťazec s minimálnou dĺžkou {2} a maximálnou dĺžkou {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "V poli {0} nie je platná plne kvalifikovaná adresa http, https alebo ftp URL.",
"The field {0} is invalid.": "Pole {0} je neplatné.",
"The value '{0}' is invalid.": "Hodnota '{0}' je neplatná.",
"The field {0} must be a number.": "Pole {0} musí byť číslo.",
"The field must be a number.": "Pole musí byť číslo.",
"ThisFieldIsNotAValidCreditCardNumber.": "V tomto poli nie je platné číslo kreditnej karty.",
"ThisFieldIsNotValid.": "Toto pole nie je platné.",
"ThisFieldIsNotAValidEmailAddress.": "V tomto poli nie je platná e-mailová adresa.",

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sl.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Polje {0} mora biti niz z najmanjšo dolžino {2} in največjo dolžino {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Polje {0} ni veljaven popolnoma kvalificiran URL http, https ali ftp.",
"The field {0} is invalid.": "Polje {0} ni veljavno.",
"The value '{0}' is invalid.": "Vrednost '{0}' ni veljavna.",
"The field {0} must be a number.": "Polje {0} mora biti številka.",
"The field must be a number.": "Polje mora biti številka.",
"ThisFieldIsNotAValidCreditCardNumber.": "To polje ni veljavna številka kreditne kartice.",
"ThisFieldIsNotValid.": "To polje ni veljavno.",
"ThisFieldIsNotAValidEmailAddress.": "To polje ni veljaven e-poštni naslov.",

5
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "{0} alanı en az {2}, en fazla {1} uzunluğunda bir metin olmalıdır.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} alanı geçerli bir http, https, ya da ftp adresi olmalıdır.",
"The field {0} is invalid.": "{0} alanı geçerli değil.",
"The value '{0}' is invalid.": "{0} değeri geçerli değil.",
"The field {0} must be a number.": "{0} alanı bir sayı olmalıdır.",
"The field must be a number.": "Bu alan bir sayı olmalıdır.",
"ThisFieldIsNotAValidCreditCardNumber.": "Bu alan geçerli bir kredi kartı numarası olmalıdır.",
"ThisFieldIsNotValid.": "Bu alan geçerli değil.",
"ThisFieldIsNotAValidEmailAddress.": "Bu alan geçerli bir e-posta adresi olmalıdır.",
@ -33,4 +36,4 @@
"ThisFieldMustBeGreaterThanOrEqual{0}": "Bu alan {0}'dan büyük veya eşit olmalıdır.",
"ThisFieldMustBeLessOrEqual{0}": "Bu alan {0}'dan küçük veya eşit olmalıdır."
}
}
}

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Trường {0} phải là một chuỗi với độ dài tối thiểu {2} và tối đa là {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Trường {0} không phải là một http, https, hoặc ftp URL đủ điều kiện hợp lệ.",
"The field {0} is invalid.": "Trường {0} không có hiệu lực",
"The value '{0}' is invalid.": "Giá trị '{0}' không hợp lệ.",
"The field {0} must be a number.": "Trường {0} phải là một số.",
"The field must be a number.": "Trường phải là một số.",
"ThisFieldIsNotAValidCreditCardNumber.": "Trường này không phải là số thẻ tín dụng hợp lệ.",
"ThisFieldIsNotValid.": "Trường này không hợp lệ.",
"ThisFieldIsNotAValidEmailAddress.": "Trường này không phải là địa chỉ e-mail hợp lệ.",

5
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "字段{0}必须是最小长度为{2}并且最大长度{1}的字符串.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "字段{0}不是有效的完全限定的http,https或ftp URL.",
"The field {0} is invalid.": "字段{0}是无效值.",
"The value '{0}' is invalid.": "'{0}'是无效值",
"The field {0} must be a number.": "字段{0}必须是数字.",
"The field must be a number.": "该字段必须是数字.",
"ThisFieldIsNotAValidCreditCardNumber.": "字段不是有效的信用卡号码.",
"ThisFieldIsNotValid.": "验证未通过.",
"ThisFieldIsNotAValidEmailAddress.": "字段不是有效的邮箱地址.",
@ -33,4 +36,4 @@
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "字段{0}不是有效的完全限定的http,https或ftp URL.",
"ThisFieldIsInvalid.": "该字段无效."
}
}
}

3
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json

@ -16,6 +16,9 @@
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "欄位{0}必須是最小長度為{2}並且最大長度{1}的字串.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "欄位{0}不是有效的完全限定的http,https或ftp URL.",
"The field {0} is invalid.": "此欄位{0}是無效值.",
"The value '{0}' is invalid.": "'{0}'是無效值",
"The field {0} must be a number.": "欄位{0}必須是號碼.",
"The field must be a number.": "此欄位{0}必須是號碼.",
"ThisFieldIsNotAValidCreditCardNumber.": "此欄位不是有效的信用卡號碼.",
"ThisFieldIsNotValid.": "此驗證未通過.",
"ThisFieldIsNotAValidEmailAddress.": "此欄位不是有效的郵箱地址.",

2
framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs

@ -14,7 +14,7 @@ public class AbpAuthorizationTestModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(onServiceRegistredContext =>
context.Services.OnRegistered(onServiceRegistredContext =>
{
if (typeof(IMyAuthorizedService1).IsAssignableFrom(onServiceRegistredContext.ImplementationType) &&
!DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType))

2
framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs

@ -19,7 +19,7 @@ public abstract class AbpInterceptionTestBase<TStartupModule> : AbpAsyncIntegrat
services.AddTransient<SimpleResultCacheTestInterceptor>();
services.AddTransient<CachedTestObject>();
services.OnRegistred(registration =>
services.OnRegistered(registration =>
{
if (typeof(SimpleInterceptionTargetClass) == registration.ImplementationType)
{

2
framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs

@ -109,7 +109,7 @@ public class ApplicationService_FluentValidation_Tests : AbpIntegratedTest<Appli
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(onServiceRegistredContext =>
context.Services.OnRegistered(onServiceRegistredContext =>
{
if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType) &&
!DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType))

2
framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs

@ -201,7 +201,7 @@ public class ApplicationService_Validation_Tests : AbpIntegratedTest<Application
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(onServiceRegistredContext =>
context.Services.OnRegistered(onServiceRegistredContext =>
{
if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType) &&
!DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType))

30
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json

@ -3,16 +3,16 @@
"texts": {
"Menu:Account": "帳號",
"UserName": "使用者名稱",
"EmailAddress": "電子信箱地址",
"UserNameOrEmailAddress": "使用者名稱或電子信箱地址",
"EmailAddress": "電子郵件地址",
"UserNameOrEmailAddress": "使用者名稱或電子郵件地址",
"Password": "密碼",
"RememberMe": "記住我",
"UseAnotherServiceToLogin": "使用另一個服務登入",
"UserLockedOutMessage": "登入失敗,使用者帳號已被鎖定.請稍後再試.",
"InvalidUserNameOrPassword": "使用者名稱或密碼錯誤!",
"LoginIsNotAllowed": "無法登入!你的賬號未激活或者需要驗證郵箱地址/手機號碼.",
"LoginIsNotAllowed": "無法登入!你的帳號未啟用或者需要驗證電子郵件地址/手機號碼.",
"SelfRegistrationDisabledMessage": "應用程式未開放註冊,請聯絡管理員以加入新使用者.",
"LocalLoginDisabledMessage": "應用程序未開放本地賬戶登錄.",
"LocalLoginDisabledMessage": "應用程式未開放本地帳戶登入.",
"Login": "登入",
"Cancel": "取消",
"Register": "註冊",
@ -26,11 +26,11 @@
"DisplayName:NewPasswordConfirm": "確認新密碼",
"PasswordChangedMessage": "你的密碼已修改成功.",
"DisplayName:UserName": "使用者名稱",
"DisplayName:Email": "電子信箱",
"DisplayName:Email": "電子郵件信箱",
"DisplayName:Name": "名字",
"DisplayName:Surname": "姓",
"DisplayName:Password": "密碼",
"DisplayName:EmailAddress": "電子信箱地址",
"DisplayName:EmailAddress": "電子郵件地址",
"DisplayName:PhoneNumber": "電話號碼",
"PersonalSettings": "個人設置",
"PersonalSettingsSaved": "個人設置已保存",
@ -38,32 +38,32 @@
"NewPasswordConfirmFailed": "請確認新密碼",
"NewPasswordSameAsOld": "新密碼不能與舊密碼相同",
"Manage": "管理",
"MyAccount": "我的戶",
"MyAccount": "我的戶",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "啟用自行註冊",
"Description:Abp.Account.IsSelfRegistrationEnabled": "是否允許使用者自行註冊帳號.",
"DisplayName:Abp.Account.EnableLocalLogin": "使用本地帳號進行身分驗證",
"Description:Abp.Account.EnableLocalLogin": "伺服器是否允許使用者使用本地帳號進行身份驗證。",
"LoggedOutTitle": "註銷",
"LoggedOutText": "你已成功註銷並將馬上返回.",
"ReturnToText": "單擊此處返回到應用程序",
"ReturnToText": "點擊此處返回到應用程式",
"OrLoginWith": "或是登入用:",
"ForgotPassword": "忘記密碼?",
"SendPasswordResetLink_Information": "密碼重置鏈接將發送到您的電子郵件以重置密碼. 如果您在幾分鐘內沒有收到電子郵件,請重試.",
"SendPasswordResetLink_Information": "密碼重置連結將發送到您的電子郵件以重置密碼. 如果您在幾分鐘內沒有收到電子郵件,請重試.",
"PasswordResetMailSentMessage": "帳戶恢復電子郵件已發送到您的電子郵件地址. 如果您在15分鐘內未在收件箱中看到此電子郵件,請檢查垃圾郵件,並標記為非垃圾郵件.",
"ResetPassword": "重設密碼",
"ConfirmPassword": "確認密碼",
"ResetPassword_Information": "請輸入您的新密碼.",
"YourPasswordIsSuccessfullyReset": "您的密碼已經被重置成功.",
"GoToTheApplication": "轉到應用程",
"BackToLogin": "返回登",
"GoToTheApplication": "轉到應用程",
"BackToLogin": "返回登",
"ProfileTab:Password": "更改密碼",
"ProfileTab:PersonalInfo": "個人信息",
"ReturnToApplication": "返回到應用程",
"ProfileTab:PersonalInfo": "個人資訊",
"ReturnToApplication": "返回到應用程",
"Volo.Account:InvalidEmailAddress": "找不到給定的電子郵件地址:{0}",
"PasswordReset": "重設密碼",
"PasswordResetInfoInEmail": "我們收到了帳戶恢復請求!如果你發起了此請求,請單擊以下鏈接以重置密碼.",
"PasswordResetInfoInEmail": "我們收到了帳戶恢復請求!如果你發起了此請求,請點擊以下連結以重置密碼.",
"ResetMyPassword": "重置我的密碼",
"AccessDenied": "拒絕訪問!",
"AccessDeniedMessage": "您無權訪問此資源."
}
}
}

9
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs

@ -1,6 +1,8 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Account.Settings;
using Volo.Abp.Identity;
using Volo.Abp.Settings;
namespace Volo.Abp.Account.Web.Pages.Account;
@ -28,7 +30,12 @@ public class LogoutModel : AccountPageModel
return RedirectSafely(ReturnUrl, ReturnUrlHash);
}
return RedirectToPage("/Account/Login");
if (await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin))
{
return RedirectToPage("/Account/Login");
}
return RedirectToPage("/");
}
public virtual Task<IActionResult> OnPostAsync()

2
modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor

@ -13,7 +13,7 @@
{
<DynamicComponent Type="@customComponentType" />
}
else if (url != null)
else if (MenuItem.Url != null)
{
<li class="nav-item @cssClass @disabled" id="@elementId">
<a class="nav-link" href="@url" target="@MenuItem.Target">

4
modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs → modules/blogging/src/Volo.Blogging.Admin.Web/Navigation/BloggingAdminMenuContributor.cs

@ -19,9 +19,9 @@ namespace Volo.Blogging.Admin
{
var l = context.GetLocalizer<BloggingResource>();
var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"]).RequirePermissions(BloggingPermissions.Blogs.Management);
var managementRootMenuItem = new ApplicationMenuItem(BloggingAdminMenuNames.GroupName, l["Menu:BlogManagement"]).RequirePermissions(BloggingPermissions.Blogs.Management);
managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogManagement.Blogs", l["Menu:Blogs"], "~/Blogging/Admin/Blogs").RequirePermissions(BloggingPermissions.Blogs.Management));
managementRootMenuItem.AddItem(new ApplicationMenuItem(BloggingAdminMenuNames.Blogs, l["Menu:Blogs"], "~/Blogging/Admin/Blogs").RequirePermissions(BloggingPermissions.Blogs.Management));
context.Menu.GetAdministration().AddItem(managementRootMenuItem);

8
modules/blogging/src/Volo.Blogging.Admin.Web/Navigation/BloggingAdminMenuNames.cs

@ -0,0 +1,8 @@
namespace Volo.Blogging.Admin;
public class BloggingAdminMenuNames
{
public const string GroupName = "BlogManagement";
public const string Blogs = GroupName + ".Blogs";
}

6
modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Index.cshtml

@ -4,6 +4,7 @@
@using Volo.Blogging.Admin.Pages.Blogging.Admin.Blogs
@using Volo.Blogging
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
@using Volo.Blogging.Localization
@inherits Volo.Blogging.Admin.Pages.Blogging.BloggingAdminPage
@model IndexModel
@ -12,6 +13,11 @@
@{
ViewBag.PageTitle = "Blogs";
}
@inject IPageLayout PageLayout
@{
PageLayout.Content.Title = L["Blogs"].Value;
PageLayout.Content.MenuItemName = BloggingAdminMenuNames.Blogs;
}
@section scripts {
<abp-script-bundle name="@typeof(IndexModel).FullName">

4
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json

@ -214,6 +214,8 @@
"Feature:TagEnableDescription": "CMS Kit's tag system that allows tagging entities such as BlogPost.",
"DeleteBlogPostMessage": "The blog will be deleted. Are you sure?",
"CaptchaCode": "Captcha code",
"CommentTextRequired": "Comment is required"
"CommentTextRequired": "Comment is required",
"CaptchaCodeErrorMessage" : "The answer you entered for the CAPTCHA was not correct. Please try again",
"CaptchaCodeMissingMessage": "The captcha code is missing!"
}
}

15
modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaException.cs

@ -1,15 +0,0 @@
using System.Runtime.Serialization;
using Volo.Abp;
namespace Volo.CmsKit.Public.Web.Security.Captcha;
public class CaptchaException : UserFriendlyException
{
public CaptchaException(string message) : base(message)
{
}
public CaptchaException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context)
{
}
}

13
modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/SimpleMathsCaptchaGenerator.cs

@ -9,6 +9,9 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Volo.Abp;
using Volo.CmsKit.Localization;
using Microsoft.Extensions.Localization;
using Volo.Abp.DependencyInjection;
using Color = SixLabors.ImageSharp.Color;
using PointF = SixLabors.ImageSharp.PointF;
@ -17,6 +20,12 @@ namespace Volo.CmsKit.Public.Web.Security.Captcha;
public class SimpleMathsCaptchaGenerator : ISingletonDependency
{
private readonly IStringLocalizer<CmsKitResource> _localizer;
public SimpleMathsCaptchaGenerator(IStringLocalizer<CmsKitResource> localizer)
{
_localizer = localizer;
}
private static Dictionary<Guid, CaptchaRequest> Session { get; set; } = new Dictionary<Guid, CaptchaRequest>();
public CaptchaOutput Generate()
@ -74,7 +83,7 @@ public class SimpleMathsCaptchaGenerator : ISingletonDependency
var request = Session[requestId];
if (request.Output.Result != value)
{
throw new CaptchaException("The captcha code doesn't match text on the picture! Please try again.");
throw new UserFriendlyException(_localizer["CaptchaCodeErrorMessage"]);
}
}
@ -86,7 +95,7 @@ public class SimpleMathsCaptchaGenerator : ISingletonDependency
}
else
{
throw new CaptchaException("The captcha code is missing!");
throw new UserFriendlyException(_localizer["CaptchaCodeMissingMessage"]);
}
}

53
npm/ng-packs/angular.json

@ -195,14 +195,54 @@
"assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"],
"styles": [
{
"input": "node_modules/bootstrap/dist/css/bootstrap.rtl.min.css",
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.css",
"inject": false,
"bundleName": "bootstrap-rtl.min"
"bundleName": "bootstrap-dim"
},
{
"input": "node_modules/bootstrap/dist/css/bootstrap.min.css",
"inject": true,
"bundleName": "bootstrap-ltr.min"
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.css",
"inject": false,
"bundleName": "ng-bundle"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/side-menu/layout-bundle.css",
"inject": false,
"bundleName": "layout-bundle"
},
{
"input": "node_modules/@abp/ng.theme.lepton-x/assets/css/abp-bundle.css",
"inject": false,
"bundleName": "abp-bundle"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.rtl.css",
"inject": false,
"bundleName": "bootstrap-dim.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.rtl.css",
"inject": false,
"bundleName": "ng-bundle.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/side-menu/layout-bundle.rtl.css",
"inject": false,
"bundleName": "layout-bundle.rtl"
},
{
"input": "node_modules/@abp/ng.theme.lepton-x/assets/css/abp-bundle.rtl.css",
"inject": false,
"bundleName": "abp-bundle.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.rtl.css",
"inject": false,
"bundleName": "font-bundle.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.css",
"inject": false,
"bundleName": "font-bundle"
},
{
"input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
@ -234,6 +274,7 @@
"inject": false,
"bundleName": "ng-zorro-antd-tree"
},
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
"apps/dev-app/src/styles.scss"
],
"scripts": []
@ -715,7 +756,7 @@
}
},
"tags": [],
"implicitDependencies": ["core","oauth"]
"implicitDependencies": ["core", "oauth"]
}
}
}

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

@ -4,7 +4,8 @@ import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config';
import { ThemeBasicModule } from '@abp/ng.theme.basic';
import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x';
import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@ -34,7 +35,8 @@ import { AbpOAuthModule } from '@abp/ng.oauth';
TenantManagementConfigModule.forRoot(),
FeatureManagementModule.forRoot(),
SettingManagementConfigModule.forRoot(),
ThemeBasicModule.forRoot(),
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
],
providers: [APP_ROUTE_PROVIDER],
declarations: [AppComponent],

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

@ -26,8 +26,8 @@
<h3>Let's improve your application!</h3>
<p>Here are some links to help you get started:</p>
</div>
<div class="card mt-4 mb-5">
<div class="card-body">
<abp-card cardClass="mt-4 mb-5">
<abp-card-body>
<div class="row text-center justify-content-md-center">
<ng-container
*ngTemplateOutlet="
@ -182,16 +182,15 @@
"
></ng-container>
</div>
</div>
</div>
</abp-card-body>
</abp-card>
<div class="mt-5 my-3 text-center">
<h3>Meet the ABP Commercial</h3>
<p>A Complete Web Application Platform Built on the ABP Framework</p>
</div>
<div class="card mt-4 mb-5">
<div class="card-body">
<abp-card cardClass="mt-4 mb-5">
<abp-card-body>
<p class="px-lg-5 mx-lg-5 py-3 text-center">
<a href="https://commercial.abp.io/" target="_blank">ABP Commercial</a> is a platform based
on the open source ABP framework. It provides pre-built application modules, rapid
@ -271,8 +270,9 @@
"
></ng-container>
</div>
</div>
</div>
</abp-card-body>
</abp-card>
<div class="mb-5 text-center">
<p class="align-middle">
<a href="https://twitter.com/abpframework" target="_blank" class="mx-2"

9
npm/ng-packs/package.json

@ -129,10 +129,13 @@
"typescript": "4.8.4",
"zone.js": "0.11.4"
},
"dependencies": {},
"dependencies": {
"@abp/ng.theme.lepton-x": "^2.1.0-rc.1",
"bootstrap-icons": "^1.10.3"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": [
"**/*.{js,jsx,ts,tsx,html,css,scss}": [
"npx prettier --write --config .prettierrc "
]
}
}
}

20
npm/ng-packs/packages/identity/src/lib/components/users/users.component.html

@ -49,18 +49,24 @@
*ngFor="let roleGroup of roleGroups; let i = index; trackBy: trackByFn"
class="form-check mb-2"
>
<input
type="checkbox"
class="form-check-input"
[attr.id]="'roles-' + i"
<abp-checkbox
*abpReplaceableTemplate="{
inputs:{
checkboxId:'roles-' + i,
label:roles[i].name,
formControl:roleGroup.controls[roles[i].name]
},
componentKey: inputKey
}"
[checkboxId]="'roles-' + i"
[formControl]="roleGroup.controls[roles[i].name]"
/>
<label class="form-check-label" [attr.for]="'roles-' + i">{{ roles[i].name }}</label>
[label]="roles[i].name"
>
</abp-checkbox>
</div>
</ng-template>
</li>
</ul>
<div class="mt-2 fade-in-top" [ngbNavOutlet]="nav"></div>
</form>
</ng-template>

38
npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts

@ -1,12 +1,28 @@
import { ListService, PagedResultDto } from "@abp/ng.core";
import { GetIdentityUsersInput, IdentityRoleDto, IdentityUserDto, IdentityUserService } from "@abp/ng.identity/proxy";
import { ePermissionManagementComponents } from "@abp/ng.permission-management";
import { Confirmation, ConfirmationService, ToasterService } from "@abp/ng.theme.shared";
import { EXTENSIONS_IDENTIFIER, FormPropData, generateFormFromProps } from "@abp/ng.theme.shared/extensions";
import { Component, Injector, OnInit, TemplateRef, TrackByFunction, ViewChild } from "@angular/core";
import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from "@angular/forms";
import { finalize, switchMap, tap } from "rxjs/operators";
import { eIdentityComponents } from "../../enums/components";
import { ListService, PagedResultDto } from '@abp/ng.core';
import {
GetIdentityUsersInput,
IdentityRoleDto,
IdentityUserDto,
IdentityUserService,
} from '@abp/ng.identity/proxy';
import { ePermissionManagementComponents } from '@abp/ng.permission-management';
import {Confirmation, ConfirmationService, eFormComponets, ToasterService} from '@abp/ng.theme.shared';
import {
EXTENSIONS_IDENTIFIER,
FormPropData,
generateFormFromProps,
} from '@abp/ng.theme.shared/extensions';
import {
Component,
Injector,
OnInit,
TemplateRef,
TrackByFunction,
ViewChild,
} from '@angular/core';
import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { finalize, switchMap, tap } from 'rxjs/operators';
import { eIdentityComponents } from '../../enums/components';
@Component({
selector: 'abp-users',
@ -43,7 +59,9 @@ export class UsersComponent implements OnInit {
permissionManagementKey = ePermissionManagementComponents.PermissionManagement;
entityDisplayName?: string;
entityDisplayName: string;
inputKey=eFormComponets.FormCheckboxComponent
trackByFn: TrackByFunction<AbstractControl> = (index, item) => Object.keys(item)[0] || index;

30
npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts

@ -81,7 +81,7 @@ export class PermissionManagementComponent
});
});
} else {
this.selectedGroup = null;
this.setSelectedGroup(null);
this._visible = false;
this.visibleChange.emit(false);
}
@ -110,10 +110,22 @@ export class PermissionManagementComponent
modalBusy = false;
selectedGroupPermissions: PermissionWithStyle[] = [];
trackByFn: TrackByFunction<PermissionGroupDto> = (_, item) => item.name;
get selectedGroupPermissions(): PermissionWithStyle[] {
if (!this.selectedGroup) return [];
constructor(protected service: PermissionsService, protected configState: ConfigStateService) {}
getChecked(name: string) {
return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted;
}
setSelectedGroup(group: PermissionGroupDto) {
this.selectedGroup = group;
if (!this.selectedGroup) {
this.selectedGroupPermissions = [];
return;
}
const margin = `margin-${
(document.body.dir as LocaleDirection) === 'rtl' ? 'right' : 'left'
@ -123,7 +135,7 @@ export class PermissionManagementComponent
(this.data.groups.find(group => group.name === this.selectedGroup?.name) || {}).permissions ||
[];
return permissions.map(
this.selectedGroupPermissions = permissions.map(
permission =>
({
...permission,
@ -133,12 +145,6 @@ export class PermissionManagementComponent
);
}
constructor(protected service: PermissionsService, protected configState: ConfigStateService) {}
getChecked(name: string) {
return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted;
}
setDisabled(permissions: PermissionGrantInfoDto[]) {
if (permissions.length) {
this.disableSelectAllTab = permissions.every(
@ -248,7 +254,7 @@ export class PermissionManagementComponent
onChangeGroup(group: PermissionGroupDto) {
this.setDisabled(group.permissions);
this.selectedGroup = group;
this.setSelectedGroup(group);
this.setTabCheckboxState();
}
@ -291,8 +297,8 @@ export class PermissionManagementComponent
return this.service.get(this.providerName, this.providerKey).pipe(
tap((permissionRes: GetPermissionListResultDto) => {
this.data = permissionRes;
this.selectedGroup = permissionRes.groups[0];
this.permissions = getPermissions(permissionRes.groups);
this.setSelectedGroup(permissionRes.groups[0]);
this.disabledSelectAllInAllTabs = this.permissions.every(
per =>
per.isGranted &&

9
npm/ng-packs/packages/theme-shared/src/lib/components/card/card-body.component.ts

@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'abp-card-body',
template: `<div class="card-body">
<ng-content></ng-content>
</div>`,
})
export class CardBodyComponent {}

9
npm/ng-packs/packages/theme-shared/src/lib/components/card/card-title.component.ts

@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'abp-card-title',
template: `<div class="card-title">
<ng-content></ng-content>
</div>`,
})
export class CardTitleComponent {}

22
npm/ng-packs/packages/theme-shared/src/lib/components/card/card.component.ts

@ -0,0 +1,22 @@
import { Component, ContentChild, Input } from '@angular/core';
import { CardBodyComponent } from './card-body.component';
import { CardTitleComponent } from './card-title.component';
@Component({
selector: 'abp-card',
template: ` <div class="card" [ngClass]="cardClass" [ngStyle]="cardStyle">
<ng-content *ngIf="cardTitleTemplate" select="abp-card-title"></ng-content>
<ng-content *ngIf="cardBodyTemplate" select="abp-card-body"></ng-content>
</div>`,
})
export class CardComponent {
@Input() cardClass: string;
@Input() cardStyle: string;
@ContentChild(CardBodyComponent)
cardBodyTemplate?: CardBodyComponent;
@ContentChild(CardTitleComponent)
cardTitleTemplate?: CardTitleComponent;
}

14
npm/ng-packs/packages/theme-shared/src/lib/components/card/card.module.ts

@ -0,0 +1,14 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { CardBodyComponent } from './card-body.component';
import { CardTitleComponent } from './card-title.component';
import { CardComponent } from './card.component';
const declarationsWithExports = [CardComponent, CardBodyComponent, CardTitleComponent];
@NgModule({
declarations: [...declarationsWithExports],
imports: [CommonModule],
exports: [...declarationsWithExports],
})
export class CardModule {}

4
npm/ng-packs/packages/theme-shared/src/lib/components/card/index.ts

@ -0,0 +1,4 @@
export * from './card.module';
export * from './card.component';
export * from './card-body.component';
export * from './card-title.component';

45
npm/ng-packs/packages/theme-shared/src/lib/components/checkbox/checkbox.component.ts

@ -0,0 +1,45 @@
import { AbstractNgModelComponent } from '@abp/ng.core';
import { Component, EventEmitter, forwardRef, Injector, Input, Output } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'abp-checkbox',
template: `
<div class="mb-3">
<input
type="checkbox"
[(ngModel)]="value"
[id]="checkboxId"
[readonly]="checkboxReadonly"
[ngClass]="checkboxClass"
[ngStyle]="checkboxStyle"
(blur)="onBlur.next()"
(focus)="onFocus.next()"
>
<label *ngIf="label" [ngClass]="labelClass" [for]="checkboxId" > {{label | abpLocalization}} </label>
</div>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FormCheckboxComponent),
multi: true,
},
]
})
export class FormCheckboxComponent extends AbstractNgModelComponent {
@Input() label?: string;
@Input() labelClass = 'form-check-label';
@Input() checkboxId!: string;
@Input() checkboxStyle = '';
@Input() checkboxClass = 'form-check-input';
@Input() checkboxReadonly = false;
@Output() onBlur = new EventEmitter<void>();
@Output() onFocus = new EventEmitter<void>();
constructor(injector: Injector) {
super(injector);
}
}

46
npm/ng-packs/packages/theme-shared/src/lib/components/form-input/form-input.component.ts

@ -0,0 +1,46 @@
import { AbstractNgModelComponent } from '@abp/ng.core';
import { Component, EventEmitter, forwardRef, Injector, Input, Output } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'abp-form-input',
template: `
<div class="mb-3">
<label class= *ngIf="label" [ngClass]="labelClass" [for]="inputId" > {{label | abpLocalization}} </label>
<input
type="text"
[id]="inputId"
[placeholder]="inputPlaceholder"
[readonly]="inputReadonly"
[ngClass]="inputClass"
[ngStyle]="inputStyle"
(blur)="onBlur.next()"
(focus)="onFocus.next()"
[(ngModel)]="value">
</div>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FormInputComponent),
multi: true,
},
]
})
export class FormInputComponent extends AbstractNgModelComponent {
@Input() inputId!: string;
@Input() inputReadonly: boolean = false;
@Input() label: string = '';
@Input() labelClass = 'form-label';
@Input() inputPlaceholder: string = '';
@Input() inputType: string = 'text';
@Input() inputStyle: string = '';
@Input() inputClass: string = 'form-control';
@Output() onBlur = new EventEmitter<void>();
@Output() onFocus = new EventEmitter<void>();
constructor(injector: Injector) {
super(injector);
}
}

1
npm/ng-packs/packages/theme-shared/src/lib/components/index.ts

@ -11,3 +11,4 @@ export * from './modal/modal.component';
export * from './toast-container/toast-container.component';
export * from './toast/toast.component';
export * from './password/password.component';
export * from './card/index';

2
npm/ng-packs/packages/theme-shared/src/lib/directives/visible.directive.ts

@ -8,7 +8,7 @@ export class AbpVisibleDirective implements OnDestroy, OnInit {
conditionSubscription: Subscription | undefined;
isVisible: boolean | undefined;
@Input('abpVisible') set abpVisible(
@Input() set abpVisible(
value: boolean | Promise<boolean> | Observable<boolean> | undefined | null,
) {
this.condition$ = checkType(value);

4
npm/ng-packs/packages/theme-shared/src/lib/enums/form.ts

@ -0,0 +1,4 @@
export enum eFormComponets {
FormInputComponent = 'FormInputComponent',
FormCheckboxComponent = 'FormCheckboxComponent',
}

1
npm/ng-packs/packages/theme-shared/src/lib/enums/index.ts

@ -1 +1,2 @@
export * from './form';
export * from './route-names';

44
npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts

@ -0,0 +1,44 @@
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { FormCheckboxComponent } from '../components/checkbox/checkbox.component';
describe('FormCheckboxComponent', () => {
let spectator: SpectatorHost<FormCheckboxComponent>;
const createHost = createHostFactory(FormCheckboxComponent);
beforeEach(
() =>
(spectator = createHost(
'<abp-checkbox></abp-checkbox>',
{
hostProps: { attributes: { autofocus: '', name: 'abp-checkbox' } },
},
)),
);
it('should display the input', () => {
expect(spectator.query('input')).toBeTruthy();
});
it('should equal the default classes to form-check-input', () => {
expect(spectator.query('input')).toHaveClass('form-check-input');
});
it('should equal the default type to checkbox', () => {
expect(spectator.query('input')).toHaveAttribute('type', 'checkbox');
});
it('should be readonly when checkboxReadonly is true', () => {
spectator.component.checkboxReadonly = true;
spectator.detectComponentChanges();
expect(spectator.query('[readonly]')).toBeTruthy();
});
it('should not contain readonly when checboxReadonly is false', () => {
spectator.component.checkboxReadonly = false;
spectator.detectComponentChanges();
expect(spectator.query('[disabled]')).toBeFalsy();
});
});

46
npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts

@ -0,0 +1,46 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { FormInputComponent } from '../components/form-input/form-input.component';
describe('FormInputComponent', () => {
let spectator: SpectatorHost<FormInputComponent>;
const createHost = createHostFactory(FormInputComponent);
beforeEach(
() =>
(spectator = createHost(
'<abp-form-input></abp-form-input>',
{
hostProps: { attributes: { autofocus: '', name: 'abp-form-input' } },
},
)),
);
it('should display the input', () => {
expect(spectator.query('input')).toBeTruthy();
});
it('should equal the default classes to form-control', () => {
expect(spectator.query('input')).toHaveClass('form-control');
});
it('should equal the default type to text', () => {
expect(spectator.query('input')).toHaveAttribute('type', 'text');
});
it('should be readonly when inputReadonly is true', () => {
spectator.component.inputReadonly = true;
spectator.detectComponentChanges();
expect(spectator.query('[readonly]')).toBeTruthy();
});
it('should not contain readonly when inputReadonly is false', () => {
spectator.component.inputReadonly = false;
spectator.detectComponentChanges();
expect(spectator.query('[disabled]')).toBeFalsy();
});
});

17
npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts

@ -36,7 +36,10 @@ import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.t
import { DateParserFormatter } from './utils/date-parser-formatter';
import { CONFIRMATION_ICONS, DEFAULT_CONFIRMATION_ICONS } from './tokens/confirmation-icons.token';
import { PasswordComponent } from './components/password/password.component';
import { CardModule } from './components/card/card.module';
import { AbpVisibleDirective } from './directives';
import { FormInputComponent } from './components/form-input/form-input.component';
import { FormCheckboxComponent } from './components/checkbox/checkbox.component';
const declarationsWithExports = [
BreadcrumbComponent,
@ -54,6 +57,8 @@ const declarationsWithExports = [
LoadingDirective,
ModalCloseDirective,
AbpVisibleDirective,
FormInputComponent,
FormCheckboxComponent
];
@NgModule({
@ -63,12 +68,20 @@ const declarationsWithExports = [
NgxValidateCoreModule,
NgbPaginationModule,
EllipsisModule,
CardModule,
],
declarations: [...declarationsWithExports, HttpErrorWrapperComponent],
exports: [NgxDatatableModule, EllipsisModule, NgxValidateCoreModule, ...declarationsWithExports],
exports: [
NgxDatatableModule,
EllipsisModule,
NgxValidateCoreModule,
...declarationsWithExports,
CardModule,
],
providers: [DatePipe],
})
export class BaseThemeSharedModule {}
export class BaseThemeSharedModule { }
@NgModule({
imports: [BaseThemeSharedModule],

4
npm/ng-packs/tsconfig.base.json

@ -30,6 +30,7 @@
"@abp/ng.identity": ["packages/identity/src/public-api.ts"],
"@abp/ng.identity/config": ["packages/identity/config/src/public-api.ts"],
"@abp/ng.identity/proxy": ["packages/identity/proxy/src/public-api.ts"],
"@abp/ng.oauth": ["packages/oauth/src/public-api.ts"],
"@abp/ng.permission-management": ["packages/permission-management/src/public-api.ts"],
"@abp/ng.permission-management/proxy": [
"packages/permission-management/proxy/src/public-api.ts"
@ -43,8 +44,7 @@
"@abp/ng.theme.basic/testing": ["packages/theme-basic/testing/src/public-api.ts"],
"@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"],
"@abp/ng.theme.shared/extensions": ["packages/theme-shared/extensions/src/public-api.ts"],
"@abp/ng.theme.shared/testing": ["packages/theme-shared/testing/src/public-api.ts"],
"@abp/ng.oauth": ["packages/oauth/src/public-api.ts"]
"@abp/ng.theme.shared/testing": ["packages/theme-shared/testing/src/public-api.ts"]
}
},
"exclude": ["node_modules", "tmp"]

6
npm/packs/flag-icon-css/abp.resourcemapping.js

@ -1,6 +0,0 @@
module.exports = {
mappings: {
"@node_modules/flag-icon-css/css/*": "@libs/flag-icon-css/css",
"@node_modules/flag-icon-css/flags/1x1/*": "@libs/flag-icon-css/flags/1x1"
}
}

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

Loading…
Cancel
Save