Browse Source

Merge branch 'dev' into EditionId

pull/22632/head
maliming 1 year ago
parent
commit
9c8d166700
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 4
      .github/workflows/angular.yml
  2. 2
      Directory.Packages.props
  3. 1
      docs/en/cli/index.md
  4. 5
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpBlazorWebAppServiceCollectionExtensions.cs
  5. 15
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProvider.cs
  6. 8
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProviderCompatible.cs
  7. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css
  8. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.min.css
  9. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.scss
  10. 2
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs
  11. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs
  12. 20
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs
  13. 25
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs
  14. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs
  15. 22
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs
  16. 43
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs
  17. 47
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs
  18. 17
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializer.cs
  19. 13
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpGuidCustomBsonTypeMapper.cs
  20. 11
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs
  21. 17
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestBase.cs
  22. 52
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpNewtonsoftSerializerProviderTests.cs
  23. 47
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs
  24. 5
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/InputAndOutputDateTimeFormat_Tests.cs
  25. 11
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs
  26. 37
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs
  27. 21
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json
  28. 18
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs
  29. 4
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj
  30. 2
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/AbpAspNetCoreMvcUiThemeBasicDemoModule.cs
  31. 32
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Program.cs
  32. 17
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Properties/launchSettings.json
  33. 18
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Startup.cs
  34. 47
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/index.js
  35. 12
      modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/Components/FeatureManagementModal.razor
  36. 11
      modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/Components/FeatureManagementModal.razor.cs
  37. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs
  38. 61
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml
  39. 20
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml.cs
  40. 1
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/feature-management-modal.css
  41. 17
      modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor
  42. 2
      modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs
  43. 17
      modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.css
  44. 2
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs
  45. 4
      npm/ng-packs/packages/account/config/src/providers/route.provider.ts
  46. 15
      npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html
  47. 9
      npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts
  48. 1
      npm/ng-packs/packages/theme-basic/src/lib/pipes/index.ts
  49. 20
      npm/ng-packs/packages/theme-basic/src/lib/pipes/lazy-translate.pipe.ts
  50. 2
      npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts
  51. 1
      npm/ng-packs/packages/theme-basic/src/public-api.ts

4
.github/workflows/angular.yml

@ -27,12 +27,12 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/cache@v2 - uses: actions/cache@v4
with: with:
path: 'npm/ng-packs/node_modules' path: 'npm/ng-packs/node_modules'
key: ${{ runner.os }}-${{ hashFiles('npm/ng-packs/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('npm/ng-packs/yarn.lock') }}
- uses: actions/cache@v2 - uses: actions/cache@v4
with: with:
path: 'templates/app/angular/node_modules' path: 'templates/app/angular/node_modules'
key: ${{ runner.os }}-${{ hashFiles('templates/app/angular/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('templates/app/angular/yarn.lock') }}

2
Directory.Packages.props

@ -119,7 +119,7 @@
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.6.0" />
<PackageVersion Include="Minio" Version="6.0.3" /> <PackageVersion Include="Minio" Version="6.0.3" />
<PackageVersion Include="MongoDB.Driver" Version="3.1.0" /> <PackageVersion Include="MongoDB.Driver" Version="3.3.0" />
<PackageVersion Include="NEST" Version="7.17.5" /> <PackageVersion Include="NEST" Version="7.17.5" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" /> <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Nito.AsyncEx.Context" Version="5.1.2" /> <PackageVersion Include="Nito.AsyncEx.Context" Version="5.1.2" />

1
docs/en/cli/index.md

@ -342,6 +342,7 @@ Note that this command can upgrade your solution from a previous version, and al
* `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default. * `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default.
* `--check-all`: Check the new version of each package separately. Default is `false`. * `--check-all`: Check the new version of each package separately. Default is `false`.
* `--version` or `-v`: Specifies the version to use for update. If not specified, latest version is used. * `--version` or `-v`: Specifies the version to use for update. If not specified, latest version is used.
* * `--leptonx-version` or `-lv`: Specifies the LeptonX version to use for update. If not specified, latest version or the version that is compatible with `--version` argument is used.
### clean ### clean

5
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpBlazorWebAppServiceCollectionExtensions.cs

@ -1,3 +1,4 @@
using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
@ -19,10 +20,14 @@ public static class AbpBlazorWebAppServiceCollectionExtensions
return services; return services;
} }
[Obsolete("Use AddBlazorWebAppServices instead. See https://github.com/abpframework/abp/issues/22622")]
public static IServiceCollection AddBlazorWebAppTieredServices([NotNull] this IServiceCollection services) public static IServiceCollection AddBlazorWebAppTieredServices([NotNull] this IServiceCollection services)
{ {
Check.NotNull(services, nameof(services)); Check.NotNull(services, nameof(services));
// Compatibility with old template code
services.AddTransient<AddBlazorWebAppTieredServicesHasBeenCalled>();
services.AddScoped<AuthenticationStateProvider, RemoteAuthenticationStateProvider>(); services.AddScoped<AuthenticationStateProvider, RemoteAuthenticationStateProvider>();
services.Replace(ServiceDescriptor.Singleton<IAbpAccessTokenProvider, PersistentComponentStateAbpAccessTokenProvider>()); services.Replace(ServiceDescriptor.Singleton<IAbpAccessTokenProvider, PersistentComponentStateAbpAccessTokenProvider>());

15
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProvider.cs

@ -1,5 +1,7 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Components.WebAssembly.WebApp; namespace Volo.Abp.AspNetCore.Components.WebAssembly.WebApp;
@ -8,21 +10,26 @@ public class RemoteAuthenticationStateProvider : AuthenticationStateProvider
{ {
protected ICurrentPrincipalAccessor CurrentPrincipalAccessor { get; } protected ICurrentPrincipalAccessor CurrentPrincipalAccessor { get; }
protected WebAssemblyCachedApplicationConfigurationClient WebAssemblyCachedApplicationConfigurationClient { get; } protected WebAssemblyCachedApplicationConfigurationClient WebAssemblyCachedApplicationConfigurationClient { get; }
protected IServiceProvider ServiceProvider { get; }
public RemoteAuthenticationStateProvider( public RemoteAuthenticationStateProvider(
ICurrentPrincipalAccessor currentPrincipalAccessor, ICurrentPrincipalAccessor currentPrincipalAccessor,
WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient) WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient,
IServiceProvider serviceProvider)
{ {
CurrentPrincipalAccessor = currentPrincipalAccessor; CurrentPrincipalAccessor = currentPrincipalAccessor;
WebAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient; WebAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient;
ServiceProvider = serviceProvider;
} }
public async override Task<AuthenticationState> GetAuthenticationStateAsync() public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{ {
if (CurrentPrincipalAccessor.Principal.Identity == null || if (ServiceProvider.GetService<AddBlazorWebAppTieredServicesHasBeenCalled>() != null)
!CurrentPrincipalAccessor.Principal.Identity.IsAuthenticated)
{ {
await WebAssemblyCachedApplicationConfigurationClient.InitializeAsync(); if (CurrentPrincipalAccessor.Principal.Identity == null || !CurrentPrincipalAccessor.Principal.Identity.IsAuthenticated)
{
await WebAssemblyCachedApplicationConfigurationClient.InitializeAsync();
}
} }
return new AuthenticationState(CurrentPrincipalAccessor.Principal); return new AuthenticationState(CurrentPrincipalAccessor.Principal);

8
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProviderCompatible.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Components.WebAssembly.WebApp;
/// <summary>
/// This class is used to indicate that the AddBlazorWebAppTieredServices method has been called for compatibility with the old template code
/// </summary>
internal sealed class AddBlazorWebAppTieredServicesHasBeenCalled
{
}

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css

@ -10,3 +10,8 @@
.dataTable tbody tr td div.dropdown ul.dropdown-menu li { .dataTable tbody tr td div.dropdown ul.dropdown-menu li {
cursor: pointer; } cursor: pointer; }
.abp-action-button ui.dropdown-menu[data-popper-reference-hidden]{
visibility: hidden;
pointer-events: none;
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.min.css

@ -1 +1 @@
.dataTable{width:100% !important;border-spacing:0 !important;}.table td,.table th{padding:8px 10px;}.dataTable tbody tr td button{cursor:pointer;}.dataTable tbody tr td div.dropdown ul.dropdown-menu li{cursor:pointer;} .dataTable{width:100%!important;border-spacing:0!important}.table td,.table th{padding:8px 10px}.dataTable tbody tr td button{cursor:pointer}.dataTable tbody tr td div.dropdown ul.dropdown-menu li{cursor:pointer}.abp-action-button ui.dropdown-menu[data-popper-reference-hidden]{visibility:hidden;pointer-events:none}

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.scss

@ -27,3 +27,10 @@
} }
} }
} }
.abp-action-button {
ui.dropdown-menu[data-popper-reference-hidden] {
visibility: hidden;
pointer-events: none;
}
}

2
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs

@ -115,7 +115,7 @@ public static class AbpApplicationBuilderExtensions
return app.UseMiddleware<AbpExceptionHandlingMiddleware>(); return app.UseMiddleware<AbpExceptionHandlingMiddleware>();
} }
[Obsolete("Replace with AbpClaimsTransformation")] [Obsolete("Use the TransformAbpClaims extension method from IServiceCollection instead.")]
public static IApplicationBuilder UseAbpClaimsMap(this IApplicationBuilder app) public static IApplicationBuilder UseAbpClaimsMap(this IApplicationBuilder app)
{ {
return app.UseMiddleware<AbpClaimsMapMiddleware>(); return app.UseMiddleware<AbpClaimsMapMiddleware>();

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs

@ -11,7 +11,7 @@ using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Security.Claims; namespace Volo.Abp.AspNetCore.Security.Claims;
[Obsolete("Replace with AbpClaimsTransformation")] [Obsolete("Use the TransformAbpClaims extension method from IServiceCollection instead.")]
public class AbpClaimsMapMiddleware : AbpMiddlewareBase, ITransientDependency public class AbpClaimsMapMiddleware : AbpMiddlewareBase, ITransientDependency
{ {
public async override Task InvokeAsync(HttpContext context, RequestDelegate next) public async override Task InvokeAsync(HttpContext context, RequestDelegate next)

20
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs

@ -2,24 +2,24 @@
public static class CliConsts public static class CliConsts
{ {
public const string Command = "AbpCliCommand"; public static string Command = "AbpCliCommand";
public const string BranchPrefix = "branch@"; public static string BranchPrefix = "branch@";
public const string DocsLink = "https://abp.io/docs"; public static string DocsLink = "https://abp.io/docs";
public const string HttpClientName = "AbpHttpClient"; public static string HttpClientName = "AbpHttpClient";
public const string GithubHttpClientName = "GithubHttpClient"; public static string GithubHttpClientName = "GithubHttpClient";
public const string LogoutUrl = CliUrls.WwwAbpIo + "api/license/logout"; public static string LogoutUrl = CliUrls.WwwAbpIo + "api/license/logout";
public const string LicenseCodePlaceHolder = @"<LICENSE_CODE/>"; public static string LicenseCodePlaceHolder = @"<LICENSE_CODE/>";
public const string AppSettingsJsonFileName = "appsettings.json"; public static string AppSettingsJsonFileName = "appsettings.json";
public static string AppSettingsSecretJsonFileName = "appsettings.secrets.json";
public const string AppSettingsSecretJsonFileName = "appsettings.secrets.json";
public static class MemoryKeys public static class MemoryKeys
{ {
public const string LatestCliVersionCheckDate = "LatestCliVersionCheckDate"; public const string LatestCliVersionCheckDate = "LatestCliVersionCheckDate";

25
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs

@ -5,19 +5,18 @@ namespace Volo.Abp.Cli;
public static class CliUrls public static class CliUrls
{ {
public const string WwwAbpIo = WwwAbpIoProduction; public static string WwwAbpIo = WwwAbpIoProduction;
public const string AccountAbpIo = AccountAbpIoProduction; public static string AccountAbpIo = AccountAbpIoProduction;
public const string NuGetRootPath = NuGetRootPathProduction; public static string NuGetRootPath = NuGetRootPathProduction;
public const string LatestVersionCheckFullPath = public static string LatestVersionCheckFullPath = "https://raw.githubusercontent.com/abpframework/abp/dev/latest-versions.json";
"https://raw.githubusercontent.com/abpframework/abp/dev/latest-versions.json";
public static string WwwAbpIoProduction = "https://abp.io/";
public const string WwwAbpIoProduction = "https://abp.io/"; public static string AccountAbpIoProduction = "https://account.abp.io/";
public const string AccountAbpIoProduction = "https://account.abp.io/"; public static string NuGetRootPathProduction = "https://nuget.abp.io/";
public const string NuGetRootPathProduction = "https://nuget.abp.io/";
public static string WwwAbpIoDevelopment = "https://localhost:44328/";
public const string WwwAbpIoDevelopment = "https://localhost:44328/"; public static string AccountAbpIoDevelopment = "https://localhost:44333/";
public const string AccountAbpIoDevelopment = "https://localhost:44333/"; public static string NuGetRootPathDevelopment = "https://localhost:44373/";
public const string NuGetRootPathDevelopment = "https://localhost:44373/";
public static string GetNuGetServiceIndexUrl(string apiKey) public static string GetNuGetServiceIndexUrl(string apiKey)
{ {

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs

@ -68,7 +68,7 @@ public class HelpCommand : IConsoleCommand, ITransientDependency
sb.AppendLine("Command List:"); sb.AppendLine("Command List:");
sb.AppendLine(""); sb.AppendLine("");
foreach (var command in AbpCliOptions.Commands.ToArray().Where(NotHiddenFromCommandList)) foreach (var command in AbpCliOptions.Commands.ToArray().Where(NotHiddenFromCommandList).OrderBy(x => x.Key))
{ {
var method = command.Value.GetMethod("GetShortDescription", BindingFlags.Static | BindingFlags.Public); var method = command.Value.GetMethod("GetShortDescription", BindingFlags.Static | BindingFlags.Public);
if (method == null) if (method == null)

22
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs

@ -38,24 +38,25 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
var directory = commandLineArgs.Options.GetOrNull(Options.SolutionPath.Short, Options.SolutionPath.Long) ?? var directory = commandLineArgs.Options.GetOrNull(Options.SolutionPath.Short, Options.SolutionPath.Long) ??
Directory.GetCurrentDirectory(); Directory.GetCurrentDirectory();
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
var leptonXVersion = commandLineArgs.Options.GetOrNull(Options.LeptonXVersion.Short, Options.LeptonXVersion.Long);
if (updateNuget || !updateNpm) if (updateNuget || !updateNpm)
{ {
await UpdateNugetPackages(commandLineArgs, directory, version); await UpdateNugetPackages(commandLineArgs, directory, version, leptonXVersion);
} }
if (updateNpm || !updateNuget) if (updateNpm || !updateNuget)
{ {
await UpdateNpmPackages(directory, version); await UpdateNpmPackages(directory, version, leptonXVersion);
} }
} }
private async Task UpdateNpmPackages(string directory, string version) private async Task UpdateNpmPackages(string directory, string version, string leptonXVersion)
{ {
await _npmPackagesUpdater.Update(directory, version: version); await _npmPackagesUpdater.Update(directory, version: version, leptonXVersion: leptonXVersion);
} }
private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory, string version) private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory, string version, string leptonXVersion)
{ {
var solutions = new List<string>(); var solutions = new List<string>();
var givenSolution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long); var givenSolution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long);
@ -77,7 +78,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
{ {
var solutionName = Path.GetFileName(solution).RemovePostFix(".sln"); var solutionName = Path.GetFileName(solution).RemovePostFix(".sln");
await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll, version: version); await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll, version: version, leptonXVersion: leptonXVersion);
Logger.LogInformation("Volo packages are updated in {SolutionName} solution", solutionName); Logger.LogInformation("Volo packages are updated in {SolutionName} solution", solutionName);
} }
@ -90,7 +91,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
{ {
var projectName = Path.GetFileName(project).RemovePostFix(".csproj"); var projectName = Path.GetFileName(project).RemovePostFix(".csproj");
await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll, version: version); await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll, version: version, leptonXVersion: leptonXVersion);
Logger.LogInformation("Volo packages are updated in {ProjectName} project", projectName); Logger.LogInformation("Volo packages are updated in {ProjectName} project", projectName);
return; return;
@ -120,6 +121,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
sb.AppendLine("-sn|--solution-name (Specify the solution name)"); sb.AppendLine("-sn|--solution-name (Specify the solution name)");
sb.AppendLine("--check-all (Check the new version of each package separately)"); sb.AppendLine("--check-all (Check the new version of each package separately)");
sb.AppendLine("-v|--version <version> (default: latest version)"); sb.AppendLine("-v|--version <version> (default: latest version)");
sb.AppendLine("-lv|--leptonx-version <version> (default: latest LeptonX version)");
sb.AppendLine(""); sb.AppendLine("");
sb.AppendLine("Some examples:"); sb.AppendLine("Some examples:");
sb.AppendLine(""); sb.AppendLine("");
@ -167,5 +169,11 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
public const string Short = "v"; public const string Short = "v";
public const string Long = "version"; public const string Long = "version";
} }
public static class LeptonXVersion
{
public const string Short = "lv";
public const string Long = "leptonx-version";
}
} }
} }

43
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs

@ -50,7 +50,7 @@ public class NpmPackagesUpdater : ITransientDependency
public async Task Update(string rootDirectory, bool includePreviews = false, public async Task Update(string rootDirectory, bool includePreviews = false,
bool includeReleaseCandidates = false, bool includeReleaseCandidates = false,
bool switchToStable = false, string version = null, bool includePreRc = false) bool switchToStable = false, string version = null, string leptonXVersion = null, bool includePreRc = false)
{ {
var fileList = _packageJsonFileFinder.Find(rootDirectory); var fileList = _packageJsonFileFinder.Find(rootDirectory);
@ -80,6 +80,7 @@ public class NpmPackagesUpdater : ITransientDependency
var updated = await UpdatePackagesInFile(file, includePreviews, includeReleaseCandidates, var updated = await UpdatePackagesInFile(file, includePreviews, includeReleaseCandidates,
switchToStable, switchToStable,
version, version,
leptonXVersion,
includePreRc); includePreRc);
packagesUpdated.TryAdd(file, updated); packagesUpdated.TryAdd(file, updated);
@ -162,6 +163,7 @@ public class NpmPackagesUpdater : ITransientDependency
bool includeReleaseCandidates = false, bool includeReleaseCandidates = false,
bool switchToStable = false, bool switchToStable = false,
string specifiedVersion = null, string specifiedVersion = null,
string specifiedLeptonXVersion = null,
bool includePreRc = false) bool includePreRc = false)
{ {
var packagesUpdated = false; var packagesUpdated = false;
@ -177,7 +179,7 @@ public class NpmPackagesUpdater : ITransientDependency
foreach (var abpPackage in abpPackages) foreach (var abpPackage in abpPackages)
{ {
var updated = await TryUpdatingPackage(filePath, abpPackage, includePreviews, includeReleaseCandidates, var updated = await TryUpdatingPackage(filePath, abpPackage, includePreviews, includeReleaseCandidates,
switchToStable, specifiedVersion, includePreRc); switchToStable, specifiedVersion, specifiedLeptonXVersion, includePreRc);
if (updated) if (updated)
{ {
@ -188,7 +190,7 @@ public class NpmPackagesUpdater : ITransientDependency
var updatedContent = packageJson.ToString(Formatting.Indented); var updatedContent = packageJson.ToString(Formatting.Indented);
File.WriteAllText(filePath, updatedContent); File.WriteAllText(filePath, updatedContent);
return packagesUpdated; return packagesUpdated;
} }
@ -199,6 +201,7 @@ public class NpmPackagesUpdater : ITransientDependency
bool includeReleaseCandidates = false, bool includeReleaseCandidates = false,
bool switchToStable = false, bool switchToStable = false,
string specifiedVersion = null, string specifiedVersion = null,
string specifiedLeptonXVersion = null,
bool includePreRc = false) bool includePreRc = false)
{ {
var currentVersion = (string)package.Value; var currentVersion = (string)package.Value;
@ -207,18 +210,36 @@ public class NpmPackagesUpdater : ITransientDependency
if (!specifiedVersion.IsNullOrWhiteSpace()) if (!specifiedVersion.IsNullOrWhiteSpace())
{ {
if (!SpecifiedVersionExists(specifiedVersion, package)) if (package.Name.IndexOf("leptonx", StringComparison.InvariantCultureIgnoreCase) > 0 && !specifiedLeptonXVersion.IsNullOrWhiteSpace())
{ {
return false; if (!SpecifiedVersionExists(specifiedLeptonXVersion, package))
} {
return false;
}
if (SemanticVersion.Parse(specifiedVersion) <= if (SemanticVersion.Parse(specifiedLeptonXVersion) <=
SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^"))) SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^")))
{ {
return false; return false;
}
version = specifiedLeptonXVersion.EnsureStartsWith('^');
} }
else
{
if (!SpecifiedVersionExists(specifiedVersion, package))
{
return false;
}
version = specifiedVersion.EnsureStartsWith('^'); if (SemanticVersion.Parse(specifiedVersion) <=
SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^")))
{
return false;
}
version = specifiedVersion.EnsureStartsWith('^');
}
} }
else else
{ {

47
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs

@ -27,7 +27,14 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
Logger = NullLogger<VoloNugetPackagesVersionUpdater>.Instance; Logger = NullLogger<VoloNugetPackagesVersionUpdater>.Instance;
} }
public async Task UpdateSolutionAsync(string solutionPath, bool includePreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false, string version = null) public async Task UpdateSolutionAsync(
string solutionPath,
bool includePreviews = false,
bool includeReleaseCandidates = false,
bool switchToStable = false,
bool checkAll = false,
string version = null,
string leptonXVersion = null)
{ {
var projectPaths = ProjectFinder.GetProjectFiles(solutionPath); var projectPaths = ProjectFinder.GetProjectFiles(solutionPath);
@ -58,6 +65,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
latestReleaseCandidateVersionInfo.Version, latestReleaseCandidateVersionInfo.Version,
latestVersionFromMyGet, latestVersionFromMyGet,
version, version,
leptonXVersion,
latestStableVersions: latestStableVersions); latestStableVersions: latestStableVersions);
fs.Seek(0, SeekOrigin.Begin); fs.Seek(0, SeekOrigin.Begin);
@ -75,7 +83,14 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
} }
} }
public async Task UpdateProjectAsync(string projectPath, bool includeNightlyPreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false, string version = null) public async Task UpdateProjectAsync(
string projectPath,
bool includeNightlyPreviews = false,
bool includeReleaseCandidates = false,
bool switchToStable = false,
bool checkAll = false,
string version = null,
string leptonXVersion = null)
{ {
if (checkAll && version.IsNullOrWhiteSpace()) if (checkAll && version.IsNullOrWhiteSpace())
{ {
@ -102,6 +117,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
latestReleaseCandidateVersionInfo.Version, latestReleaseCandidateVersionInfo.Version,
latestVersionFromMyGet, latestVersionFromMyGet,
version, version,
leptonXVersion,
latestStableVersions: latestStableVersions); latestStableVersions: latestStableVersions);
fs.Seek(0, SeekOrigin.Begin); fs.Seek(0, SeekOrigin.Begin);
@ -166,6 +182,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
SemanticVersion latestNugetReleaseCandidateVersion = null, SemanticVersion latestNugetReleaseCandidateVersion = null,
string latestMyGetVersion = null, string latestMyGetVersion = null,
string specifiedVersion = null, string specifiedVersion = null,
string specifiedLeptonXVersion = null,
List<PackageVersionCheckerService.LatestStableVersionResult> latestStableVersions = null) List<PackageVersionCheckerService.LatestStableVersionResult> latestStableVersions = null)
{ {
string packageId = null; string packageId = null;
@ -222,21 +239,35 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
var leptonXPackageVersion = latestStableVersions? var leptonXPackageVersion = latestStableVersions?
.FirstOrDefault(v => v.Version.Equals(specifiedVersion, StringComparison.InvariantCultureIgnoreCase))?.LeptonX?.Version; .FirstOrDefault(v => v.Version.Equals(specifiedVersion, StringComparison.InvariantCultureIgnoreCase))?.LeptonX?.Version;
if ((isLeptonXPackage && string.IsNullOrWhiteSpace(leptonXPackageVersion)) || isStudioPackage) if ((isLeptonXPackage && string.IsNullOrWhiteSpace(leptonXPackageVersion) && specifiedLeptonXVersion.IsNullOrWhiteSpace()) || isStudioPackage)
{ {
Logger.LogWarning("Package: {PackageId} could not be updated. Please manually update the package version yourself to prevent version mismatches!", packageId); Logger.LogWarning("Package: {PackageId} could not be updated. Please manually update the package version yourself to prevent version mismatches!", packageId);
continue; continue;
} }
var isLeptonXPackageWithVersion = isLeptonXPackage && !string.IsNullOrWhiteSpace(leptonXPackageVersion); if (isLeptonXPackage)
if (isLeptonXPackageWithVersion || await SpecifiedVersionExists(specifiedVersion, packageId))
{ {
TryUpdatingPackage(isLeptonXPackageWithVersion ? leptonXPackageVersion : specifiedVersion); var isLeptonXPackageWithVersion = isLeptonXPackage && !string.IsNullOrWhiteSpace(leptonXPackageVersion);
if (isLeptonXPackageWithVersion || await SpecifiedVersionExists(specifiedLeptonXVersion, packageId))
{
TryUpdatingPackage(specifiedLeptonXVersion ?? leptonXPackageVersion);
}
else
{
Logger.LogWarning($"Package \"{packageId}\" specified version v{specifiedLeptonXVersion} does not exist!");
}
} }
else else
{ {
Logger.LogWarning("Package \"{PackageId}\" specified version v{SpecifiedVersion} does not exist!", packageId, specifiedVersion); if (await SpecifiedVersionExists(specifiedVersion, packageId))
{
TryUpdatingPackage(specifiedVersion);
}
else
{
Logger.LogWarning($"Package \"{packageId}\" specified version v{specifiedVersion} does not exist!");
}
} }
void TryUpdatingPackage(string versionToUpdate) void TryUpdatingPackage(string versionToUpdate)

17
framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializer.cs

@ -40,21 +40,10 @@ public class AbpSystemTextJsonSerializer : IJsonSerializer, ITransientDependency
camelCase, camelCase,
indented, indented,
Options.JsonSerializerOptions Options.JsonSerializerOptions
}, _ => }, _ => new JsonSerializerOptions(Options.JsonSerializerOptions)
{ {
var settings = new JsonSerializerOptions(Options.JsonSerializerOptions); PropertyNamingPolicy = camelCase ? JsonNamingPolicy.CamelCase : null,
WriteIndented = indented
if (camelCase)
{
settings.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
}
if (indented)
{
settings.WriteIndented = true;
}
return settings;
}); });
} }
} }

13
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpGuidCustomBsonTypeMapper.cs

@ -0,0 +1,13 @@
using System;
using MongoDB.Bson;
namespace Volo.Abp.MongoDB;
public class AbpGuidCustomBsonTypeMapper : ICustomBsonTypeMapper
{
public bool TryMapToBsonValue(object value, out BsonValue bsonValue)
{
bsonValue = new BsonBinaryData((Guid)value, GuidRepresentation.Standard);
return true;
}
}

11
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization;
@ -16,6 +17,12 @@ namespace Volo.Abp.MongoDB;
[DependsOn(typeof(AbpDddDomainModule))] [DependsOn(typeof(AbpDddDomainModule))]
public class AbpMongoDbModule : AbpModule public class AbpMongoDbModule : AbpModule
{ {
static AbpMongoDbModule()
{
BsonSerializer.TryRegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));
BsonTypeMapper.RegisterCustomTypeMapper(typeof(Guid), new AbpGuidCustomBsonTypeMapper());
}
public override void PreConfigureServices(ServiceConfigurationContext context) public override void PreConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.AddConventionalRegistrar(new AbpMongoDbConventionalRegistrar()); context.Services.AddConventionalRegistrar(new AbpMongoDbConventionalRegistrar());
@ -23,8 +30,6 @@ public class AbpMongoDbModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
BsonSerializer.TryRegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));
context.Services.TryAddTransient( context.Services.TryAddTransient(
typeof(IMongoDbContextProvider<>), typeof(IMongoDbContextProvider<>),
typeof(UnitOfWorkMongoDbContextProvider<>) typeof(UnitOfWorkMongoDbContextProvider<>)

17
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestBase.cs

@ -1,4 +1,9 @@
using Volo.Abp.Testing; using System;
using System.Collections.Concurrent;
using System.Reflection;
using Newtonsoft.Json;
using Volo.Abp.Json.Newtonsoft;
using Volo.Abp.Testing;
namespace Volo.Abp.Json; namespace Volo.Abp.Json;
@ -12,6 +17,16 @@ public abstract class AbpJsonSystemTextJsonTestBase : AbpIntegratedTest<AbpJsonS
public abstract class AbpJsonNewtonsoftJsonTestBase : AbpIntegratedTest<AbpJsonNewtonsoftTestModule> public abstract class AbpJsonNewtonsoftJsonTestBase : AbpIntegratedTest<AbpJsonNewtonsoftTestModule>
{ {
protected AbpJsonNewtonsoftJsonTestBase()
{
var cache = typeof(AbpNewtonsoftJsonSerializer).GetField("JsonSerializerOptionsCache", BindingFlags.NonPublic | BindingFlags.Static);
if (cache != null)
{
var cacheValue = cache.GetValue(null)?.As<ConcurrentDictionary<object, JsonSerializerSettings>>();
cacheValue?.Clear();
}
}
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{ {
options.UseAutofac(); options.UseAutofac();

52
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpNewtonsoftSerializerProviderTests.cs

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using Shouldly;
using Xunit;
namespace Volo.Abp.Json;
[Collection("AbpJsonNewtonsoftJsonTest")]
public class AbpNewtonsoftSerializerProviderTests : AbpJsonNewtonsoftJsonTestBase
{
protected IJsonSerializer JsonSerializer;
public AbpNewtonsoftSerializerProviderTests()
{
JsonSerializer = GetRequiredService<IJsonSerializer>();
}
public class File
{
public string FileName { get; set; }
public Dictionary<string, int> ExtraProperties { get; set; }
}
[Fact]
public void Serialize_Deserialize_Test()
{
var defaultIndent = " "; // Default indent is 2 spaces
var newLine = Environment.NewLine;
var file = new File()
{
FileName = "abp",
ExtraProperties = new Dictionary<string, int>()
{
{ "One", 1 },
{ "Two", 2 }
}
};
var json = JsonSerializer.Serialize(file, camelCase: true);
json.ShouldBe("{\"fileName\":\"abp\",\"extraProperties\":{\"One\":1,\"Two\":2}}");
json = JsonSerializer.Serialize(file, camelCase: true, indented: true);
json.ShouldBe($"{{{newLine}{defaultIndent}\"fileName\": \"abp\",{newLine}{defaultIndent}\"extraProperties\": {{{newLine}{defaultIndent}{defaultIndent}\"One\": 1,{newLine}{defaultIndent}{defaultIndent}\"Two\": 2{newLine}{defaultIndent}}}{newLine}}}");
json = JsonSerializer.Serialize(file, camelCase: false);
json.ShouldBe("{\"FileName\":\"abp\",\"ExtraProperties\":{\"One\":1,\"Two\":2}}");
json = JsonSerializer.Serialize(file, camelCase: false, indented: true);
json.ShouldBe($"{{{newLine}{defaultIndent}\"FileName\": \"abp\",{newLine}{defaultIndent}\"ExtraProperties\": {{{newLine}{defaultIndent}{defaultIndent}\"One\": 1,{newLine}{defaultIndent}{defaultIndent}\"Two\": 2{newLine}{defaultIndent}}}{newLine}}}");
}
}

47
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs

@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Shouldly; using Shouldly;
using Volo.Abp.Data; using Volo.Abp.Data;
@ -11,11 +13,11 @@ namespace Volo.Abp.Json;
public abstract class AbpSystemTextJsonSerializerProviderTestBase : AbpJsonSystemTextJsonTestBase public abstract class AbpSystemTextJsonSerializerProviderTestBase : AbpJsonSystemTextJsonTestBase
{ {
protected AbpSystemTextJsonSerializer JsonSerializer; protected IJsonSerializer JsonSerializer;
public AbpSystemTextJsonSerializerProviderTestBase() public AbpSystemTextJsonSerializerProviderTestBase()
{ {
JsonSerializer = GetRequiredService<AbpSystemTextJsonSerializer>(); JsonSerializer = GetRequiredService<IJsonSerializer>();
} }
public class TestExtensibleObjectClass : ExtensibleObject public class TestExtensibleObjectClass : ExtensibleObject
@ -23,6 +25,13 @@ public abstract class AbpSystemTextJsonSerializerProviderTestBase : AbpJsonSyste
public string Name { get; set; } public string Name { get; set; }
} }
public class File
{
public string FileName { get; set; }
public Dictionary<string, int> ExtraProperties { get; set; }
}
public class FileWithBoolean public class FileWithBoolean
{ {
public string Name { get; set; } public string Name { get; set; }
@ -74,6 +83,34 @@ public abstract class AbpSystemTextJsonSerializerProviderTestBase : AbpJsonSyste
public class AbpSystemTextJsonSerializerProviderTests : AbpSystemTextJsonSerializerProviderTestBase public class AbpSystemTextJsonSerializerProviderTests : AbpSystemTextJsonSerializerProviderTestBase
{ {
[Fact]
public void Serialize_Deserialize_Test()
{
var defaultIndent = " "; // Default indent is 2 spaces
var newLine = Environment.NewLine;
var file = new File()
{
FileName = "abp",
ExtraProperties = new Dictionary<string, int>()
{
{ "One", 1 },
{ "Two", 2 }
}
};
var json = JsonSerializer.Serialize(file, camelCase: true);
json.ShouldBe("{\"fileName\":\"abp\",\"extraProperties\":{\"One\":1,\"Two\":2}}");
json = JsonSerializer.Serialize(file, camelCase: true, indented: true);
json.ShouldBe($"{{{newLine}{defaultIndent}\"fileName\": \"abp\",{newLine}{defaultIndent}\"extraProperties\": {{{newLine}{defaultIndent}{defaultIndent}\"One\": 1,{newLine}{defaultIndent}{defaultIndent}\"Two\": 2{newLine}{defaultIndent}}}{newLine}}}");
json = JsonSerializer.Serialize(file, camelCase: false);
json.ShouldBe("{\"FileName\":\"abp\",\"ExtraProperties\":{\"One\":1,\"Two\":2}}");
json = JsonSerializer.Serialize(file, camelCase: false, indented: true);
json.ShouldBe($"{{{newLine}{defaultIndent}\"FileName\": \"abp\",{newLine}{defaultIndent}\"ExtraProperties\": {{{newLine}{defaultIndent}{defaultIndent}\"One\": 1,{newLine}{defaultIndent}{defaultIndent}\"Two\": 2{newLine}{defaultIndent}}}{newLine}}}");
}
[Fact] [Fact]
public void Serialize_Deserialize_With_Boolean() public void Serialize_Deserialize_With_Boolean()
{ {
@ -223,6 +260,8 @@ public class AbpSystemTextJsonSerializerProviderDateTimeFormatTests : AbpSystemT
options.InputDateTimeFormats.Add("yyyy*MM*dd"); options.InputDateTimeFormats.Add("yyyy*MM*dd");
options.OutputDateTimeFormat = "yyyy*MM*dd HH*mm*ss"; options.OutputDateTimeFormat = "yyyy*MM*dd HH*mm*ss";
}); });
base.AfterAddApplication(services);
} }
[Fact] [Fact]
@ -289,6 +328,8 @@ public class AbpSystemTextJsonSerializerProviderDatetimeKindUtcTests : AbpSystem
{ {
Kind = DateTimeKind.Utc; Kind = DateTimeKind.Utc;
services.Configure<AbpClockOptions>(x => x.Kind = Kind); services.Configure<AbpClockOptions>(x => x.Kind = Kind);
base.AfterAddApplication(services);
} }
} }
@ -298,6 +339,8 @@ public class AbpSystemTextJsonSerializerProviderDatetimeKindLocalTests : AbpSyst
{ {
Kind = DateTimeKind.Local; Kind = DateTimeKind.Local;
services.Configure<AbpClockOptions>(x => x.Kind = Kind); services.Configure<AbpClockOptions>(x => x.Kind = Kind);
base.AfterAddApplication(services);
} }
} }

5
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/InputAndOutputDateTimeFormat_Tests.cs

@ -31,6 +31,8 @@ public class InputAndOutputDateTimeFormatSystemTextJsonTests : AbpJsonSystemText
{ {
options.Kind = DateTimeKind.Utc; options.Kind = DateTimeKind.Utc;
}); });
base.AfterAddApplication(services);
} }
[Fact] [Fact]
@ -59,6 +61,7 @@ public class InputAndOutputDateTimeFormatSystemTextJsonTests : AbpJsonSystemText
} }
} }
[Collection("AbpJsonNewtonsoftJsonTest")]
public class InputAndOutputDateTimeFormatNewtonsoftTests : AbpJsonNewtonsoftJsonTestBase public class InputAndOutputDateTimeFormatNewtonsoftTests : AbpJsonNewtonsoftJsonTestBase
{ {
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
@ -83,6 +86,8 @@ public class InputAndOutputDateTimeFormatNewtonsoftTests : AbpJsonNewtonsoftJson
{ {
options.Kind = DateTimeKind.Utc; options.Kind = DateTimeKind.Utc;
}); });
base.AfterAddApplication(services);
} }
[Fact] [Fact]

11
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MongoDB.Driver.Linq; using MongoDB.Driver.Linq;
using Shouldly; using Shouldly;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.TestApp; using Volo.Abp.TestApp;
using Volo.Abp.TestApp.Domain; using Volo.Abp.TestApp.Domain;
@ -52,10 +53,12 @@ public class Repository_Basic_Tests : Repository_Basic_Tests<AbpMongoDbTestModul
} }
[Fact] [Fact]
public override async Task InsertAsync() public async override Task InsertAsync()
{ {
var person = new Person(Guid.NewGuid(), "New Person", 35); var person = new Person(Guid.NewGuid(), "New Person", 35);
person.Phones.Add(new Phone(person.Id, "1234567890")); person.Phones.Add(new Phone(person.Id, "1234567890"));
person.SetProperty("test-guid-property", person.Id);
person.SetProperty("test-nullable-guid-property", (Guid?)person.Id);
await PersonRepository.InsertAsync(person); await PersonRepository.InsertAsync(person);
@ -64,15 +67,17 @@ public class Repository_Basic_Tests : Repository_Basic_Tests<AbpMongoDbTestModul
person.Name.ShouldBe("New Person"); person.Name.ShouldBe("New Person");
person.Phones.Count.ShouldBe(1); person.Phones.Count.ShouldBe(1);
person.Phones.Any(p => p.PersonId == person.Id && p.Number == "1234567890").ShouldBeTrue(); person.Phones.Any(p => p.PersonId == person.Id && p.Number == "1234567890").ShouldBeTrue();
person.GetProperty<Guid>("test-guid-property").ShouldBe(person.Id);
person.GetProperty<Guid?>("test-nullable-guid-property").ShouldBe(person.Id);
} }
[Fact] [Fact]
public async Task Filter_Case_Insensitive() public async Task Filter_Case_Insensitive()
{ {
(await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "ISTANBUL").ShouldBeNull(); (await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "ISTANBUL").ShouldBeNull();
(await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "istanbul").ShouldBeNull(); (await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "istanbul").ShouldBeNull();
(await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "Istanbul").ShouldNotBeNull(); (await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "Istanbul").ShouldNotBeNull();
(await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "douglas").ShouldNotBeNull(); (await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "douglas").ShouldNotBeNull();
(await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "DOUGLAS").ShouldNotBeNull(); (await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "DOUGLAS").ShouldNotBeNull();
(await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "Douglas").ShouldNotBeNull(); (await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "Douglas").ShouldNotBeNull();

37
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs

@ -1,31 +1,42 @@
using System; using System;
using System.IO; using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Serilog; using Serilog;
using Serilog.Events;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo;
public class Program public class Program
{ {
public static int Main(string[] args) public async static Task<int> Main(string[] args)
{ {
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug() //TODO: Should be configurable! .MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.File("Logs/logs.txt") .WriteTo.Async(c => c.Console())
.CreateLogger(); .CreateLogger();
try try
{ {
Log.Information("Starting web host."); Log.Information("Starting web host.");
CreateHostBuilder(args).Build().Run(); var builder = WebApplication.CreateBuilder(args);
builder.Host.AddAppSettingsSecretsJson()
.UseAutofac()
.UseSerilog();
await builder.AddApplicationAsync<AbpAspNetCoreMvcUiBootstrapDemoModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
return 0; return 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
if (ex is HostAbortedException)
{
throw;
}
Log.Fatal(ex, "Host terminated unexpectedly!"); Log.Fatal(ex, "Host terminated unexpectedly!");
return 1; return 1;
} }
@ -34,14 +45,4 @@ public class Program
Log.CloseAndFlush(); Log.CloseAndFlush();
} }
} }
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseAutofac()
.UseSerilog();
} }

21
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json

@ -1,27 +1,12 @@
{ {
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51339",
"sslPort": 0
}
},
"profiles": { "profiles": {
"IIS Express": { "Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Volo.AbpIo.Commercial.Web": {
"commandName": "Project", "commandName": "Project",
"launchBrowser": true, "launchBrowser": true,
"applicationUrl": "http://localhost:5000", "applicationUrl": "https://localhost:5000",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
} }
} }
} }

18
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs

@ -1,18 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<AbpAspNetCoreMvcUiBootstrapDemoModule>();
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.InitializeApplication();
}
}

4
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj

@ -10,7 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" />
<PackageReference Include="Serilog.AspNetCore" /> <PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="Serilog.Sinks.File" /> <PackageReference Include="Serilog.Sinks.Async" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -23,5 +23,5 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
</Project> </Project>

2
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/AbpAspNetCoreMvcUiThemeBasicDemoModule.cs

@ -55,8 +55,8 @@ public class AbpAspNetCoreMvcUiThemeBasicDemoModule : AbpModule
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
app.MapAbpStaticAssets();
app.UseRouting(); app.UseRouting();
app.MapAbpStaticAssets();
app.UseConfiguredEndpoints(); app.UseConfiguredEndpoints();
} }
} }

32
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Program.cs

@ -1,30 +1,42 @@
using System; using System;
using Microsoft.AspNetCore.Hosting; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Serilog; using Serilog;
using Serilog.Events;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo; namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo;
public class Program public class Program
{ {
public static int Main(string[] args) public async static Task<int> Main(string[] args)
{ {
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug() .MinimumLevel.Debug()
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console()) .WriteTo.Async(c => c.Console())
.CreateLogger(); .CreateLogger();
try try
{ {
Log.Information("Starting web host."); Log.Information("Starting web host.");
CreateHostBuilder(args).Build().Run(); var builder = WebApplication.CreateBuilder(args);
builder.Host.AddAppSettingsSecretsJson()
.UseAutofac()
.UseSerilog();
await builder.AddApplicationAsync<AbpAspNetCoreMvcUiThemeBasicDemoModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
return 0; return 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
if (ex is HostAbortedException)
{
throw;
}
Log.Fatal(ex, "Host terminated unexpectedly!"); Log.Fatal(ex, "Host terminated unexpectedly!");
return 1; return 1;
} }
@ -33,14 +45,4 @@ public class Program
Log.CloseAndFlush(); Log.CloseAndFlush();
} }
} }
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseAutofac()
.UseSerilog();
} }

17
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Properties/launchSettings.json

@ -1,24 +1,9 @@
{ {
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:61659",
"sslPort": 0
}
},
"profiles": { "profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo": { "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo": {
"commandName": "Project", "commandName": "Project",
"launchBrowser": true, "launchBrowser": true,
"applicationUrl": "http://localhost:5000", "applicationUrl": "https://localhost:5001",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }

18
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Startup.cs

@ -1,18 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<AbpAspNetCoreMvcUiThemeBasicDemoModule>();
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.InitializeApplication();
}
}

47
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/index.js

@ -2,7 +2,11 @@ $(function () {
var l = abp.localization.getResource('Docs'); var l = abp.localization.getResource('Docs');
var service = window.volo.docs.admin.documentsAdmin; var service = window.volo.docs.admin.documentsAdmin;
moment.localeData().preparse = (s)=>s; var getFormattedDate = function ($datePicker) {
return $datePicker.data('date');
};
moment.localeData().preparse = (s)=>s;
moment.localeData().postformat = (s)=>s; moment.localeData().postformat = (s)=>s;
var singleDatePicker = $('#DocumentsContainer .singledatepicker'); var singleDatePicker = $('#DocumentsContainer .singledatepicker');
@ -26,7 +30,7 @@ $(function () {
var comboboxItems = []; var comboboxItems = [];
service.getFilterItems() service.getFilterItems()
.then(function (result) { .then(function (result) {
comboboxItems = result; comboboxItems = result;
@ -41,13 +45,13 @@ $(function () {
$projectId.on('change', function () { $projectId.on('change', function () {
fillOptions(); fillOptions();
}); });
var comboboxs = { var comboboxs = {
version: $('#Version'), version: $('#Version'),
languageCode: $('#LanguageCode'), languageCode: $('#LanguageCode'),
format: $('#Format') format: $('#Format')
}; };
for (var key in comboboxs) { for (var key in comboboxs) {
comboboxs[key].on('change', function () { comboboxs[key].on('change', function () {
fillOptions(); fillOptions();
@ -62,7 +66,7 @@ $(function () {
comboboxs[key].empty(); comboboxs[key].empty();
} }
} }
function getSelectedItem() { function getSelectedItem() {
var item = {}; var item = {};
for (var key in comboboxs) { for (var key in comboboxs) {
@ -70,13 +74,13 @@ $(function () {
} }
return item; return item;
} }
function SetComboboxsValues(item) { function SetComboboxsValues(item) {
for (var key in comboboxs) { for (var key in comboboxs) {
comboboxs[key].val(item[key]); comboboxs[key].val(item[key]);
} }
} }
function addComboboxsEmptyItem() { function addComboboxsEmptyItem() {
for (var key in comboboxs) { for (var key in comboboxs) {
comboboxs[key].append($('<option/>').val('').text('')); comboboxs[key].append($('<option/>').val('').text(''));
@ -84,17 +88,17 @@ $(function () {
} }
function fillOptions() { function fillOptions() {
selectedItem = getSelectedItem(); selectedItem = getSelectedItem();
var selectedProjectId = $projectId.val(); var selectedProjectId = $projectId.val();
emptyComboboxs(); emptyComboboxs();
addComboboxsEmptyItem(); addComboboxsEmptyItem();
var selectedProjectItems = comboboxItems.filter((item) => !selectedProjectId || item.projectId === selectedProjectId); var selectedProjectItems = comboboxItems.filter((item) => !selectedProjectId || item.projectId === selectedProjectId);
for (var key in selectedItem) { for (var key in selectedItem) {
var item = selectedProjectItems.find((item) => item[key] === selectedItem[key]); var item = selectedProjectItems.find((item) => item[key] === selectedItem[key]);
if (item) { if (item) {
@ -103,14 +107,14 @@ $(function () {
selectedItem[key] = ''; selectedItem[key] = '';
} }
} }
selectedProjectItems.forEach(function (item) { selectedProjectItems.forEach(function (item) {
for (var key in comboboxs) { for (var key in comboboxs) {
appendComboboxItem(comboboxs[key], item, key); appendComboboxItem(comboboxs[key], item, key);
} }
}); });
SetComboboxsValues(selectedItem); SetComboboxsValues(selectedItem);
} }
function appendComboboxItem($combobox, item , key) { function appendComboboxItem($combobox, item , key) {
@ -128,21 +132,20 @@ $(function () {
var getFilter = function () { var getFilter = function () {
$('#DocumentsContainer').handleDatepicker('.singledatepicker'); $('#DocumentsContainer').handleDatepicker('.singledatepicker');
return { return {
projectId: $('#ProjectId').val(), projectId: $('#ProjectId').val(),
name: $('#Name').val(), name: $('#Name').val(),
version: $('#Version').val(), version: $('#Version').val(),
languageCode: $('#LanguageCode').val(), languageCode: $('#LanguageCode').val(),
format: $('#Format').val(), format: $('#Format').val(),
creationTimeMin: $('#DocumentsContainer').find('input[name="CreationTimeMin"'), creationTimeMin: getFormattedDate($('#DocumentsContainer').find('input[name="CreationTimeMin"]')),
creationTimeMax: $('#DocumentsContainer').find('input[name="CreationTimeMax"'), creationTimeMax: getFormattedDate($('#DocumentsContainer').find('input[name="CreationTimeMax"]')),
lastUpdatedTimeMin: $('#DocumentsContainer').find('input[name="LastUpdatedTimeMin"'), lastUpdatedTimeMin: getFormattedDate($('#DocumentsContainer').find('input[name="LastUpdatedTimeMin"]')),
lastUpdatedTimeMax: $('#DocumentsContainer').find('input[name="LastUpdatedTimeMax"'), lastUpdatedTimeMax: getFormattedDate($('#DocumentsContainer').find('input[name="LastUpdatedTimeMax"]')),
lastSignificantUpdateTimeMin: $('#DocumentsContainer').find('input[name="LastSignificantUpdateTimeMin"'), lastSignificantUpdateTimeMin: getFormattedDate($('#DocumentsContainer').find('input[name="LastSignificantUpdateTimeMin"]')),
lastSignificantUpdateTimeMax: $('#DocumentsContainer').find('input[name="LastSignificantUpdateTimeMax"'), lastSignificantUpdateTimeMax: getFormattedDate($('#DocumentsContainer').find('input[name="LastSignificantUpdateTimeMax"]')),
lastCachedTimeMin: $('#DocumentsContainer').find('input[name="LastCachedTimeMin"'), lastCachedTimeMin: getFormattedDate($('#DocumentsContainer').find('input[name="LastCachedTimeMin"]')),
lastCachedTimeMax: $('#DocumentsContainer').find('input[name="LastCachedTimeMax"'), lastCachedTimeMax: getFormattedDate($('#DocumentsContainer').find('input[name="LastCachedTimeMax"]')),
}; };
}; };

12
modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/Components/FeatureManagementModal.razor

@ -35,12 +35,12 @@
{ {
<div class="mt-2"> <div class="mt-2">
@{ @{
var disabled = IsDisabled(feature.Provider.Name); var disabled = IsDisabled(feature);
if (feature.ValueType is FreeTextStringValueType) if (feature.ValueType is FreeTextStringValueType)
{ {
<Field Style="@GetFeatureStyles(feature)"> <Field Style="@GetFeatureStyles(feature)">
<FieldLabel>@feature.DisplayName</FieldLabel> <FieldLabel>@GetShownName(feature)</FieldLabel>
<TextEdit Disabled="@disabled" <TextEdit Disabled="@disabled"
Text="@feature.Value" Text="@feature.Value"
TextChanged="@(async (v) => await OnFeatureValueChangedAsync(v, feature))" /> TextChanged="@(async (v) => await OnFeatureValueChangedAsync(v, feature))" />
@ -56,8 +56,8 @@
var items = ((SelectionStringValueType)feature.ValueType).ItemSource.Items; var items = ((SelectionStringValueType)feature.ValueType).ItemSource.Items;
var selectedValue = SelectionStringValues[feature.Name]; var selectedValue = SelectionStringValues[feature.Name];
<Field Style="@GetFeatureStyles(feature)"> <Field Style="@GetFeatureStyles(feature)">
<FieldLabel>@feature.DisplayName</FieldLabel> <FieldLabel>@GetShownName(feature)</FieldLabel>
<Select TValue="string" SelectedValue="selectedValue" SelectedValueChanged="s => SelectionStringValues[feature.Name] = s"> <Select TValue="string" Disabled="disabled" SelectedValue="selectedValue" SelectedValueChanged="s => SelectionStringValues[feature.Name] = s">
@foreach (var item in items) @foreach (var item in items)
{ {
<SelectItem Value="@item.Value"> <SelectItem Value="@item.Value">
@ -75,8 +75,8 @@
if (feature.ValueType is ToggleStringValueType) if (feature.ValueType is ToggleStringValueType)
{ {
<Field Style="@GetFeatureStyles(feature)"> <Field Style="@GetFeatureStyles(feature)">
<Check TValue="bool" Checked="@ToggleValues[feature.Name]" CheckedChanged="@(async (v) => await OnSelectedValueChangedAsync(v, feature))"> <Check TValue="bool" Disabled="disabled" Checked="@ToggleValues[feature.Name]" CheckedChanged="@(async (v) => await OnSelectedValueChangedAsync(v, feature))">
@feature.DisplayName @GetShownName(feature)
</Check> </Check>
@if (feature.Description != null) @if (feature.Description != null)
{ {

11
modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/Components/FeatureManagementModal.razor.cs

@ -150,9 +150,16 @@ public partial class FeatureManagementModal
return $"margin-left: {feature.Depth * 20}px"; return $"margin-left: {feature.Depth * 20}px";
} }
protected virtual bool IsDisabled(string providerName) protected virtual bool IsDisabled(FeatureDto feature)
{ {
return providerName != ProviderName && providerName != DefaultValueFeatureValueProvider.ProviderName; return feature.Provider.Name != ProviderName && feature.Provider.Name != DefaultValueFeatureValueProvider.ProviderName;
}
public string GetShownName(FeatureDto featureDto)
{
return !IsDisabled(featureDto)
? featureDto.DisplayName
: $"{featureDto.DisplayName} ({featureDto.Provider.Name})";
} }
protected virtual async Task OnFeatureValueChangedAsync(string value, FeatureDto feature) protected virtual async Task OnFeatureValueChangedAsync(string value, FeatureDto feature)

2
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs

@ -152,7 +152,7 @@ public class FeatureManager : IFeatureManager, ISingletonDependency
await using (await providers[0].HandleContextAsync(providerName, providerKey)) await using (await providers[0].HandleContextAsync(providerName, providerKey))
{ {
var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null); var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null);
if (fallbackValue.Value == value) if (string.Equals(fallbackValue.Value, value, StringComparison.OrdinalIgnoreCase))
{ {
//Clear the value if it's same as it's fallback value //Clear the value if it's same as it's fallback value
value = null; value = null;

61
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml

@ -45,44 +45,40 @@
@for (var j = 0; j < featureGroup.Features.Count; j++) @for (var j = 0; j < featureGroup.Features.Count; j++)
{ {
var feature = featureGroup.Features[j]; var feature = featureGroup.Features[j];
var disabled = Model.IsDisabled(feature);
<div class="mt-2"> <div class="mt-2">
<input type="text" abp-id-name="@Model.FeatureGroups[i].Features[j].IsDisabled" value="@(disabled.ToString())" hidden/>
<input type="text" abp-id-name="@Model.FeatureGroups[i].Features[j].Type" value="@feature.ValueType?.Name" hidden/> <input type="text" abp-id-name="@Model.FeatureGroups[i].Features[j].Type" value="@feature.ValueType?.Name" hidden/>
@if (feature.ValueType is ToggleStringValueType) @if (feature.ValueType is ToggleStringValueType)
{ {
<abp-input asp-for="@feature.Value" <abp-input asp-for="@feature.Value"
type="checkbox" type="checkbox"
class="d-inline" class="d-inline"
abp-id-name="@Model.FeatureGroups[i].Features[j].BoolValue" abp-id-name="@Model.FeatureGroups[i].Features[j].BoolValue"
label="@feature.DisplayName" label="@Model.GetShownName(feature)"
group-data-feature-name="@feature.Name" disabled="disabled"
group-data-parent-name="@(feature.ParentName ?? "")" group-data-feature-name="@feature.Name"
group-style="margin-inline-start: @(feature.Depth * 20)px"/> group-data-parent-name="@(feature.ParentName ?? "")"
group-style="margin-inline-start: @(feature.Depth * 20)px"/>
@if (feature.Description != null) if (feature.Description != null)
{ {
<div class="form-text" style="margin-inline-start: @(feature.Depth * 20)px">@feature.Description</div> <div class="form-text" style="margin-inline-start: @(feature.Depth * 20)px">@feature.Description</div>
} }
} }
@if (feature.ValueType is FreeTextStringValueType) @if (feature.ValueType is FreeTextStringValueType)
{ {
var type = "text"; var type = feature.ValueType.Validator is NumericValueValidator ? "number" : "text";
if(feature.ValueType.Validator is NumericValueValidator)
{
type = "number";
}
<abp-input asp-for="@feature.Value" <abp-input asp-for="@feature.Value"
label="@feature.DisplayName" label="@Model.GetShownName(feature)"
abp-id-name="@Model.FeatureGroups[i].Features[j].Value" abp-id-name="@Model.FeatureGroups[i].Features[j].Value"
type="@type" type="@type"
group-data-feature-name="@feature.Name" disabled="disabled"
group-data-parent-name="@(feature.ParentName ?? "")" group-data-feature-name="@feature.Name"
group-style="margin-inline-start: @(feature.Depth * 25)px"/> group-data-parent-name="@(feature.ParentName ?? "")"
@if (feature.Description != null) group-style="margin-inline-start: @(feature.Depth * 25)px"/>
if (feature.Description != null)
{ {
<div class="form-text" style="margin-inline-start: @(feature.Depth * 25)px">@feature.Description</div> <div class="form-text" style="margin-inline-start: @(feature.Depth * 25)px">@feature.Description</div>
} }
@ -91,19 +87,12 @@
@if (feature.ValueType is SelectionStringValueType selectType) @if (feature.ValueType is SelectionStringValueType selectType)
{ {
<div data-feature-name="@feature.Name" data-parent-name="@(feature.ParentName ?? "")" style="margin-inline-start: @(feature.Depth * 25)px" class="mb-3"> <div data-feature-name="@feature.Name" data-parent-name="@(feature.ParentName ?? "")" style="margin-inline-start: @(feature.Depth * 25)px" class="mb-3">
<label class="form-label" for="@feature.Name">@feature.DisplayName</label> <label class="form-label" for="@feature.Name">@Model.GetShownName(feature)</label>
<select id="@feature.Name" name="FeatureGroups[@i].Features[@j].Value" class="form-select" @(disabled ? "disabled" : "")>
<select id="@feature.Name" name="FeatureGroups[@i].Features[@j].Value" class="form-select">
@foreach (var item in selectType.ItemSource.Items) @foreach (var item in selectType.ItemSource.Items)
{ {
if (item.Value == feature.Value) var selected = item.Value == feature.Value ? "selected=\"selected\"" : "";
{ <!option value="@item.Value" @(selected) >@CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </!option>
<option value="@item.Value" selected="selected"> @CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </option>
}
else
{
<option value="@item.Value"> @CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </option>
}
} }
</select> </select>
@if (feature.Description != null) @if (feature.Description != null)
@ -112,8 +101,8 @@
} }
</div> </div>
} }
<input value="@feature.Name" abp-id-name="@Model.FeatureGroups[i].Features[j].Name" hidden/> <input value="@feature.Name" abp-id-name="@Model.FeatureGroups[i].Features[j].Name" hidden/>
</div> </div>
} }
</div> </div>

20
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml.cs

@ -25,9 +25,9 @@ public class FeatureManagementModal : AbpPageModel
[HiddenInput] [HiddenInput]
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public string ProviderKey { get; set; } public string ProviderKey { get; set; }
[HiddenInput] [HiddenInput]
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public string ProviderKeyDisplayName { get; set; } public string ProviderKeyDisplayName { get; set; }
[BindProperty] [BindProperty]
@ -69,7 +69,7 @@ public class FeatureManagementModal : AbpPageModel
{ {
var features = new UpdateFeaturesDto var features = new UpdateFeaturesDto
{ {
Features = FeatureGroups.SelectMany(g => g.Features).Select(f => new UpdateFeatureDto Features = FeatureGroups.SelectMany(g => g.Features).Where(x => !x.IsDisabled).Select(f => new UpdateFeatureDto
{ {
Name = f.Name, Name = f.Name,
Value = f.Type == nameof(ToggleStringValueType) ? f.BoolValue.ToString() : f.Value Value = f.Type == nameof(ToggleStringValueType) ? f.BoolValue.ToString() : f.Value
@ -85,6 +85,18 @@ public class FeatureManagementModal : AbpPageModel
return NoContent(); return NoContent();
} }
public bool IsDisabled(FeatureDto featureDto)
{
return featureDto.Provider.Name != ProviderName && featureDto.Provider.Name != DefaultValueFeatureValueProvider.ProviderName;
}
public string GetShownName(FeatureDto featureDto)
{
return !IsDisabled(featureDto)
? featureDto.DisplayName
: $"{featureDto.DisplayName} ({featureDto.Provider.Name})";
}
public class FeatureGroupViewModel public class FeatureGroupViewModel
{ {
public List<FeatureViewModel> Features { get; set; } public List<FeatureViewModel> Features { get; set; }
@ -92,6 +104,8 @@ public class FeatureManagementModal : AbpPageModel
public class FeatureViewModel public class FeatureViewModel
{ {
public bool IsDisabled { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Value { get; set; } public string Value { get; set; }

1
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/feature-management-modal.css

@ -2,7 +2,6 @@
max-height: 400px; max-height: 400px;
} }
.custom-scroll-container > .col-md-4 { .custom-scroll-container > .col-md-4 {
max-height: 500px; max-height: 500px;
} }

17
modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor

@ -37,22 +37,25 @@
@if (_groups != null && _groups.Any()) @if (_groups != null && _groups.Any())
{ {
<div class="lpx-scroll-pills-container" > <div class="lpx-scroll-pills-container" >
<Tabs @key="_groups" TabPosition="TabPosition.Start" Pills="true" SelectedTab="@_selectedTabName" SelectedTabChanged="@OnSelectedTabChangedAsync"> <Tabs @key="_groups"
VerticalItemsColumnSize="ColumnSize.Is4.OnDesktop"
TabPosition="TabPosition.Start"
Pills="true"
SelectedTab="@_selectedTabName"
SelectedTabChanged="@OnSelectedTabChangedAsync">
<Items> <Items>
@foreach (var group in _groups) @foreach (var group in _groups)
{ {
<Tab Name="@GetNormalizedGroupName(group.Name)"> <Tab Name="@GetNormalizedGroupName(group.Name)">
@if (group.Permissions.Any(x => x.IsGranted)) @if (group.Permissions.Any(x => x.IsGranted))
{ {
<span> <b>@group.DisplayName (@(group.Permissions.Count(x => x.IsGranted)))</b>
<b>@group.DisplayName ( @(group.Permissions.Count(x => x.IsGranted)) )</b>
</span>
} }
else else
{ {
<span> <small>
@group.DisplayName ( @(group.Permissions.Count(x => x.IsGranted)) ) @group.DisplayName (@(group.Permissions.Count(x => x.IsGranted)))
</span> </small>
} }
</Tab> </Tab>
} }

2
modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs

@ -58,7 +58,7 @@ public partial class PermissionManagementModal
var result = await PermissionAppService.GetAsync(_providerName, _providerKey); var result = await PermissionAppService.GetAsync(_providerName, _providerKey);
_entityDisplayName = entityDisplayName ?? result.EntityDisplayName; _entityDisplayName = entityDisplayName ?? result.EntityDisplayName;
_allGroups = result.Groups; _allGroups = result.Groups.OrderBy(x => x.DisplayName).ToList();
_groups = _allGroups.ToList(); _groups = _allGroups.ToList();
NormalizePermissionGroup(); NormalizePermissionGroup();

17
modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.css

@ -15,7 +15,7 @@ fieldset legend {
} }
::deep .lpx-scroll-pills-container ul li { ::deep .lpx-scroll-pills-container ul li {
border: 1px solid #e8eef3; border: 1px solid var(--lpx-border-color);
margin-bottom: 10px; margin-bottom: 10px;
border-radius: 10px; border-radius: 10px;
} }
@ -26,6 +26,15 @@ fieldset legend {
background-color: #6c5dd3 !important; background-color: #6c5dd3 !important;
} }
::deep .lpx-theme-dark .lpx-scroll-pills-container ul li { ::deep .nav-pills .nav-link {
border: 1px solid #23262a; background: none;
} border: 0;
border-radius: var(--bs-nav-pills-border-radius);
}
::deep .nav-pills .nav-link:disabled {
color: var(--bs-nav-link-disabled-color);
background-color: transparent;
border-color: transparent;
}

2
modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs

@ -136,7 +136,7 @@ public class SettingManager : ISettingManager, ISingletonDependency
if (providers.Count > 1 && !forceToSet && setting.IsInherited && value != null) if (providers.Count > 1 && !forceToSet && setting.IsInherited && value != null)
{ {
var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null); var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null);
if (fallbackValue == value) if (string.Equals(fallbackValue, value, StringComparison.OrdinalIgnoreCase))
{ {
//Clear the value if it's same as it's fallback value //Clear the value if it's same as it's fallback value
value = null; value = null;

4
npm/ng-packs/packages/account/config/src/providers/route.provider.ts

@ -24,12 +24,14 @@ export function configureRoutes() {
path: '/account/login', path: '/account/login',
name: eAccountRouteNames.Login, name: eAccountRouteNames.Login,
parentName: eAccountRouteNames.Account, parentName: eAccountRouteNames.Account,
layout: eLayoutType.account,
order: 1, order: 1,
}, },
{ {
path: '/account/register', path: '/account/register',
name: eAccountRouteNames.Register, name: eAccountRouteNames.Register,
parentName: eAccountRouteNames.Account, parentName: eAccountRouteNames.Account,
layout: eLayoutType.account,
order: 2, order: 2,
}, },
{ {
@ -45,12 +47,14 @@ export function configureRoutes() {
path: '/account/forgot-password', path: '/account/forgot-password',
parentName: eAccountRouteNames.Account, parentName: eAccountRouteNames.Account,
name: eAccountRouteNames.ForgotPassword, name: eAccountRouteNames.ForgotPassword,
layout: eLayoutType.account,
invisible: true, invisible: true,
}, },
{ {
path: '/account/reset-password', path: '/account/reset-password',
parentName: eAccountRouteNames.Account, parentName: eAccountRouteNames.Account,
name: eAccountRouteNames.ResetPassword, name: eAccountRouteNames.ResetPassword,
layout: eLayoutType.account,
invisible: true, invisible: true,
}, },
]); ]);

15
npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html

@ -3,8 +3,7 @@
<ng-container <ng-container
[ngTemplateOutlet]="isDropdown(route) ? dropdownLink : defaultLink" [ngTemplateOutlet]="isDropdown(route) ? dropdownLink : defaultLink"
[ngTemplateOutletContext]="{ $implicit: route }" [ngTemplateOutletContext]="{ $implicit: route }"
> />
</ng-container>
} }
<ng-template #defaultLink let-route> <ng-template #defaultLink let-route>
@ -13,7 +12,7 @@
@if (route.iconClass) { @if (route.iconClass) {
<i [ngClass]="route.iconClass"></i> <i [ngClass]="route.iconClass"></i>
} }
{{ route.name | abpLocalization }} {{ route.name | abpLazyTranslate | async }}
</a> </a>
</li> </li>
</ng-template> </ng-template>
@ -40,7 +39,7 @@
@if (route.iconClass) { @if (route.iconClass) {
<i [ngClass]="route.iconClass"></i> <i [ngClass]="route.iconClass"></i>
} }
{{ route.name | abpLocalization }} {{ route.name | abpLazyTranslate | async }}
</a> </a>
<div <div
#routeContainer #routeContainer
@ -48,9 +47,7 @@
(click)="$event.preventDefault(); $event.stopPropagation()" (click)="$event.preventDefault(); $event.stopPropagation()"
[class.d-block]="smallScreen && rootDropdownExpand[route.name]" [class.d-block]="smallScreen && rootDropdownExpand[route.name]"
> >
<ng-container <ng-container *ngTemplateOutlet="forTemplate; context: { $implicit: route }" />
*ngTemplateOutlet="forTemplate; context: { $implicit: route }"
></ng-container>
</div> </div>
</li> </li>
} }
@ -61,7 +58,7 @@
<ng-template <ng-template
[ngTemplateOutlet]="child.children?.length ? dropdownChild : defaultChild" [ngTemplateOutlet]="child.children?.length ? dropdownChild : defaultChild"
[ngTemplateOutletContext]="{ $implicit: child }" [ngTemplateOutletContext]="{ $implicit: child }"
></ng-template> />
} }
</ng-template> </ng-template>
@ -105,7 +102,7 @@
class="dropdown-menu dropdown-menu-start border-0 shadow-sm" class="dropdown-menu dropdown-menu-start border-0 shadow-sm"
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()" [class.d-block]="smallScreen && dropdownSubmenu.isOpen()"
> >
<ng-container *ngTemplateOutlet="forTemplate; context: { $implicit: child }"></ng-container> <ng-container *ngTemplateOutlet="forTemplate; context: { $implicit: child }" />
</div> </div>
</div> </div>
</ng-template> </ng-template>

9
npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts

@ -2,6 +2,7 @@ import { ABP, RoutesService, TreeNode } from '@abp/ng.core';
import { import {
Component, Component,
ElementRef, ElementRef,
inject,
Input, Input,
QueryList, QueryList,
Renderer2, Renderer2,
@ -15,6 +16,9 @@ import {
templateUrl: 'routes.component.html', templateUrl: 'routes.component.html',
}) })
export class RoutesComponent { export class RoutesComponent {
public readonly routesService = inject(RoutesService);
protected renderer = inject(Renderer2);
@Input() smallScreen?: boolean; @Input() smallScreen?: boolean;
@ViewChildren('childrenContainer') childrenContainers!: QueryList<ElementRef<HTMLDivElement>>; @ViewChildren('childrenContainer') childrenContainers!: QueryList<ElementRef<HTMLDivElement>>;
@ -23,11 +27,6 @@ export class RoutesComponent {
trackByFn: TrackByFunction<TreeNode<ABP.Route>> = (_, item) => item.name; trackByFn: TrackByFunction<TreeNode<ABP.Route>> = (_, item) => item.name;
constructor(
public readonly routesService: RoutesService,
protected renderer: Renderer2,
) {}
isDropdown(node: TreeNode<ABP.Route>) { isDropdown(node: TreeNode<ABP.Route>) {
return !node?.isLeaf || this.routesService.hasChildren(node.name); return !node?.isLeaf || this.routesService.hasChildren(node.name);
} }

1
npm/ng-packs/packages/theme-basic/src/lib/pipes/index.ts

@ -0,0 +1 @@
export * from './lazy-translate.pipe';

20
npm/ng-packs/packages/theme-basic/src/lib/pipes/lazy-translate.pipe.ts

@ -0,0 +1,20 @@
import { LocalizationService, ConfigStateService } from '@abp/ng.core';
import { inject, Pipe, PipeTransform } from '@angular/core';
import { Observable, filter, take, switchMap, shareReplay } from 'rxjs';
@Pipe({
name: 'abpLazyTranslate',
})
export class LazyTranslatePipe implements PipeTransform {
private localizationService = inject(LocalizationService);
private configStateService = inject(ConfigStateService);
transform(key: string): Observable<string> {
return this.configStateService.getAll$().pipe(
filter(config => !!config.localization),
take(1),
switchMap(() => this.localizationService.get(key)),
shareReplay({ bufferSize: 1, refCount: true }),
);
}
}

2
npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts

@ -16,6 +16,7 @@ import { PageAlertContainerComponent } from './components/page-alert-container/p
import { RoutesComponent } from './components/routes/routes.component'; import { RoutesComponent } from './components/routes/routes.component';
import { ValidationErrorComponent } from './components/validation-error/validation-error.component'; import { ValidationErrorComponent } from './components/validation-error/validation-error.component';
import { provideThemeBasicConfig } from './providers'; import { provideThemeBasicConfig } from './providers';
import { LazyTranslatePipe } from './pipes';
export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, EmptyLayoutComponent]; export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, EmptyLayoutComponent];
@ -48,6 +49,7 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt
NgbCollapseModule, NgbCollapseModule,
NgbDropdownModule, NgbDropdownModule,
NgxValidateCoreModule, NgxValidateCoreModule,
LazyTranslatePipe,
], ],
}) })
export class BaseThemeBasicModule {} export class BaseThemeBasicModule {}

1
npm/ng-packs/packages/theme-basic/src/public-api.ts

@ -6,6 +6,7 @@ export * from './lib/components';
export * from './lib/enums'; export * from './lib/enums';
export * from './lib/handlers'; export * from './lib/handlers';
export * from './lib/models'; export * from './lib/models';
export * from './lib/pipes';
export * from './lib/providers'; export * from './lib/providers';
export * from './lib/theme-basic.module'; export * from './lib/theme-basic.module';
export * from './lib/tokens'; export * from './lib/tokens';

Loading…
Cancel
Save