Browse Source

feat(localization): 重构动态多语言本地化

pull/1126/head
colin 11 months ago
parent
commit
0b18942936
  1. 3
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xml
  2. 30
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xsd
  3. 20
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN.Abp.Localization.Persistence.csproj
  4. 20
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceModule.cs
  5. 35
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceOptions.cs
  6. 135
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/DefaultStaticLocalizationSaver.cs
  7. 16
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/ILocalizationPersistenceReader.cs
  8. 26
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/ILocalizationPersistenceWriter.cs
  9. 24
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/LocalizableStringText.cs
  10. 40
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/LocalizationPersistenceContributor.cs
  11. 39
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/LocalizationSaveToPersistenceContributor.cs
  12. 32
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/NoneLocalizationPersistenceReader.cs
  13. 35
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/NoneLocalizationPersistenceWriter.cs
  14. 37
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/StaticLocalizationSaverHostService.cs
  15. 129
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/README.EN.md
  16. 129
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/README.md
  17. 44
      aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/Volo/Abp/Localization/AbpLocalizationOptionsExtensions.cs
  18. 1
      aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsEntityFrameworkCoreModule.cs
  19. 3
      aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Components/ProfileManagementGroup/TwoFactor/Default.js
  20. 74
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/AbpAuditingWebModule.cs
  21. 3
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/FodyWeavers.xml
  22. 39
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/LINGYUN.Abp.Auditing.Web.csproj
  23. 19
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/Pages/Account/Components/ProfileManagementGroup/SecurityLog/AccountProfileSecurityLogManagementGroupViewComponent.cs
  24. 22
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.cshtml
  25. 60
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.js
  26. 26
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/ProfileManagement/SecurityLogManagementPageContributor.cs
  27. 12
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/Properties/launchSettings.json
  28. 146
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/wwwroot/client-proxies/auditing-proxy.js
  29. 2
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN.Abp.LocalizationManagement.Domain.csproj
  30. 104
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementDomainModule.cs
  31. 17
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementOptions.cs
  32. 10
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationLanguageStoreCache.cs
  33. 29
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationStoreCache.cs
  34. 15
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationTextStoreCache.cs
  35. 2
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/IStaticLocalizationSaver.cs
  36. 4
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ITextRepository.cs
  37. 124
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationCacheInvalidator.cs
  38. 8
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionary.cs
  39. 6
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithCulture.cs
  40. 6
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithResource.cs
  41. 21
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageCacheInvalidator.cs
  42. 24
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageCacheItem.cs
  43. 7
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageDictionary.cs
  44. 21
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageProvider.cs
  45. 50
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageStoreCache.cs
  46. 65
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementPersistenceReader.cs
  47. 203
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementPersistenceWriter.cs
  48. 45
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationResourceContributor.cs
  49. 160
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStore.cs
  50. 12
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreCacheInitializeContext.cs
  51. 243
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreInMemoryCache.cs
  52. 25
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationTextCacheInvalidator.cs
  53. 30
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationTextCacheItem.cs
  54. 119
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationTextStoreCache.cs
  55. 309
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs
  56. 26
      aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs
  57. 68
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItem.cs
  58. 16
      aspnet-core/services/LY.MicroService.Applications.Single/DataSeeder/DataSeederWorker.cs
  59. 103
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/Components/ProfileManagementGroup/PersonalInfo/Default.cshtml
  60. 28
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/Components/ProfileManagementGroup/PersonalInfo/Default.js
  61. 17
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/EmailConfirm.cshtml
  62. 74
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/EmailConfirm.cshtml.cs
  63. 13
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/EmailConfirmConfirmation.cshtml
  64. 23
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/EmailConfirmConfirmation.cshtml.cs
  65. 26
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/SendCode.cshtml
  66. 128
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/SendCode.cshtml.cs
  67. 16
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/SendEmailConfirm.cshtml
  68. 75
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/SendEmailConfirm.cshtml.cs
  69. 65
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/TwoFactorSupportedLoginModel.cs
  70. 4
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/UseRecoveryCode.cshtml
  71. 11
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/UseRecoveryCode.cshtml.cs
  72. 26
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/VerifyAuthenticatorCode.cshtml
  73. 61
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/VerifyAuthenticatorCode.cshtml.cs
  74. 29
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/VerifyCode.cshtml
  75. 92
      aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/VerifyCode.cshtml.cs
  76. 2
      aspnet-core/services/LY.MicroService.Applications.Single/Properties/launchSettings.json
  77. 14
      aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/AuthServerHttpApiHostModule.Configure.cs
  78. 12
      aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.Configure.cs
  79. 4
      aspnet-core/services/LY.MicroService.AuthServer/LY.MicroService.AuthServer.csproj
  80. 10
      aspnet-core/services/LY.MicroService.AuthServer/gulpfile.js
  81. 3
      aspnet-core/services/LY.MicroService.AuthServer/package.json
  82. 10315
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/css/all.css
  83. 1226
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/css/v4-shims.css
  84. BIN
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf
  85. BIN
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2
  86. BIN
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf
  87. BIN
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2
  88. BIN
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf
  89. BIN
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2
  90. 79
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/abp/core/abp.js
  91. 99
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/bootstrap/css/bootstrap.rtl.min.css.map
  92. 34
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/bootstrap/js/bootstrap.bundle.min.js
  93. 396
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/datatables.net-bs5/css/dataTables.bootstrap5.css
  94. 178
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/datatables.net-bs5/js/dataTables.bootstrap5.js
  95. 15733
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/datatables.net/js/jquery.dataTables.js
  96. 11
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js
  97. 29
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation/jquery.validate.js
  98. 31
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation/localization/messages_ar.js
  99. 6
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation/localization/messages_ar.min.js
  100. 4
      aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation/localization/messages_az.min.js

3
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xml

@ -1,3 +0,0 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/FodyWeavers.xsd

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

20
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN.Abp.Localization.Persistence.csproj

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
<AssemblyName>LINGYUN.Abp.Localization.Persistence</AssemblyName>
<PackageId>LINGYUN.Abp.Localization.Persistence</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Localization" />
</ItemGroup>
</Project>

20
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceModule.cs

@ -1,20 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Localization.Persistence;
[DependsOn(
typeof(AbpLocalizationModule))]
public class AbpLocalizationPersistenceModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHostedService<StaticLocalizationSaverHostService>();
Configure<AbpLocalizationOptions>(options =>
{
options.GlobalContributors.Add<LocalizationPersistenceContributor>();
});
}
}

35
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/AbpLocalizationPersistenceOptions.cs

@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Localization.Persistence;
public class AbpLocalizationPersistenceOptions
{
public bool SaveStaticLocalizationsToPersistence { get; set; }
public HashSet<string> SaveToPersistenceResources { get; }
public AbpLocalizationPersistenceOptions()
{
SaveStaticLocalizationsToPersistence = true;
SaveToPersistenceResources = new HashSet<string>();
}
public void AddPersistenceResource<TResource>()
{
AddPersistenceResource(typeof(TResource));
}
public void AddPersistenceResource(Type resourceType)
{
var resourceName = LocalizationResourceNameAttribute.GetName(resourceType);
if (SaveToPersistenceResources.Contains(resourceName))
{
return;
}
SaveToPersistenceResources.Add(resourceName);
}
}

135
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/DefaultStaticLocalizationSaver.cs

@ -1,135 +0,0 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.Uow;
namespace LINGYUN.Abp.Localization.Persistence;
[Dependency(ReplaceServices = true)]
public class DefaultStaticLocalizationSaver : IStaticLocalizationSaver, ITransientDependency
{
protected ILocalizationPersistenceWriter LocalizationPersistenceWriter { get; }
protected AbpLocalizationOptions LocalizationOptions { get; }
protected IServiceProvider ServiceProvider { get; }
protected AbpLocalizationPersistenceOptions LocalizationPersistenceOptions { get; }
public DefaultStaticLocalizationSaver(
IServiceProvider serviceProvider,
ILocalizationPersistenceWriter localizationPersistenceWriter,
IOptions<AbpLocalizationOptions> localizationOptions,
IOptions<AbpLocalizationPersistenceOptions> localizationPersistenceOptions)
{
ServiceProvider = serviceProvider;
LocalizationPersistenceWriter = localizationPersistenceWriter;
LocalizationOptions = localizationOptions.Value;
LocalizationPersistenceOptions = localizationPersistenceOptions.Value;
}
[UnitOfWork]
public async virtual Task SaveAsync()
{
if (!LocalizationPersistenceOptions.SaveStaticLocalizationsToPersistence)
{
return;
}
var canWriterTexts = new List<LocalizableStringText>();
foreach (var localizationResource in LocalizationOptions.Resources)
{
if (ShouldSaveToPersistence(localizationResource.Value))
{
if (!await LocalizationPersistenceWriter.WriteResourceAsync(localizationResource.Value))
{
continue;
}
foreach (var language in LocalizationOptions.Languages)
{
if (!await LocalizationPersistenceWriter.WriteLanguageAsync(language))
{
continue;
}
using (CultureHelper.Use(language.CultureName, language.UiCultureName))
{
await FillCanWriterTextxAsync(localizationResource.Value, language, canWriterTexts);
}
}
}
}
if (canWriterTexts.Any())
{
await LocalizationPersistenceWriter.WriteTextsAsync(canWriterTexts);
}
}
protected virtual bool ShouldSaveToPersistence(LocalizationResourceBase localizationResource)
{
var saveResource = false;
if (localizationResource.Contributors.Exists(IsMatchSaveToPersistenceContributor))
{
saveResource = true;
}
if (!saveResource)
{
saveResource = LocalizationPersistenceOptions
.SaveToPersistenceResources
.Contains(localizationResource.ResourceName);
}
return saveResource;
}
protected virtual bool IsMatchSaveToPersistenceContributor(ILocalizationResourceContributor contributor)
{
return typeof(LocalizationSaveToPersistenceContributor).IsAssignableFrom(contributor.GetType());
}
protected async virtual Task FillCanWriterTextxAsync(
LocalizationResourceBase localizationResource,
LanguageInfo language,
List<LocalizableStringText> canWriterTexts)
{
var fillTexts = new Dictionary<string, LocalizedString>();
var context = new LocalizationResourceInitializationContext(localizationResource, ServiceProvider);
foreach (var contributor in localizationResource.Contributors)
{
if (contributor.IsDynamic)
{
continue;
}
contributor.Initialize(context);
await contributor.FillAsync(language.CultureName, fillTexts);
}
var existsKeys = await LocalizationPersistenceWriter.GetExistsTextsAsync(
localizationResource.ResourceName,
language.CultureName,
fillTexts.Values.Select(x => x.Name));
var notExistsKeys = fillTexts.Values.Where(x => !existsKeys.Contains(x.Name));
foreach (var notExistsKey in notExistsKeys)
{
if (!canWriterTexts.Any(x => x.CultureName == language.CultureName && x.Name == notExistsKey.Name))
{
canWriterTexts.Add(
new LocalizableStringText(
localizationResource.ResourceName,
language.CultureName,
notExistsKey.Name,
notExistsKey.Value));
}
}
}
}

16
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/ILocalizationPersistenceReader.cs

@ -1,16 +0,0 @@
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Localization.Persistence;
public interface ILocalizationPersistenceReader
{
LocalizedString GetOrNull(string resourceName, string cultureName, string name);
void Fill(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary);
Task FillAsync(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary);
Task<IEnumerable<string>> GetSupportedCulturesAsync();
}

26
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/ILocalizationPersistenceWriter.cs

@ -1,26 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Localization.Persistence;
public interface ILocalizationPersistenceWriter
{
Task<bool> WriteLanguageAsync(
LanguageInfo language,
CancellationToken cancellationToken = default);
Task<bool> WriteResourceAsync(
LocalizationResourceBase resource,
CancellationToken cancellationToken = default);
Task<IEnumerable<string>> GetExistsTextsAsync(
string resourceName,
string cultureName,
IEnumerable<string> keys,
CancellationToken cancellationToken = default);
Task<bool> WriteTextsAsync(
IEnumerable<LocalizableStringText> texts,
CancellationToken cancellationToken = default);
}

24
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/LocalizableStringText.cs

@ -1,24 +0,0 @@
namespace LINGYUN.Abp.Localization.Persistence;
public class LocalizableStringText
{
public LocalizableStringText(
string resourceName,
string cultureName,
string name,
string value)
{
ResourceName = resourceName;
CultureName = cultureName;
Name = name;
Value = value;
}
public string ResourceName { get; set; }
public string CultureName { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public override string ToString()
{
return $"[R]:{ResourceName},[C]:{CultureName ?? ""},[N]:{Name},[V]:{Value ?? ""}";
}
}

40
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/LocalizationPersistenceContributor.cs

@ -1,40 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Localization.Persistence;
public class LocalizationPersistenceContributor : ILocalizationResourceContributor
{
public bool IsDynamic => true;
private LocalizationResourceBase _resource;
private ILocalizationPersistenceReader _persistenceSupport;
public void Initialize(LocalizationResourceInitializationContext context)
{
_resource = context.Resource;
_persistenceSupport = context.ServiceProvider.GetRequiredService<ILocalizationPersistenceReader>();
}
public virtual void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
_persistenceSupport.Fill(_resource.ResourceName, cultureName, dictionary);
}
public async virtual Task FillAsync(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
await _persistenceSupport.FillAsync(_resource.ResourceName, cultureName, dictionary);
}
public virtual LocalizedString GetOrNull(string cultureName, string name)
{
return _persistenceSupport.GetOrNull(_resource.ResourceName, cultureName, name);
}
public async virtual Task<IEnumerable<string>> GetSupportedCulturesAsync()
{
return await _persistenceSupport.GetSupportedCulturesAsync();
}
}

39
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/LocalizationSaveToPersistenceContributor.cs

@ -1,39 +0,0 @@
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Localization.Persistence;
/// <summary>
/// 空接口, 使用此提供者可持久化本地化资源到持久设施
/// </summary>
public class LocalizationSaveToPersistenceContributor : ILocalizationResourceContributor
{
public bool IsDynamic => true;
public void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
}
public Task FillAsync(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
return Task.CompletedTask;
}
public LocalizedString GetOrNull(string cultureName, string name)
{
return null;
}
public Task<IEnumerable<string>> GetSupportedCulturesAsync()
{
IEnumerable<string> emptyCultures = new string[0];
return Task.FromResult(emptyCultures);
}
public void Initialize(LocalizationResourceInitializationContext context)
{
}
}

32
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/NoneLocalizationPersistenceReader.cs

@ -1,32 +0,0 @@
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Localization.Persistence;
[Dependency(TryRegister = true)]
public class NoneLocalizationPersistenceReader : ILocalizationPersistenceReader, ISingletonDependency
{
public void Fill(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
}
public Task FillAsync(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
return Task.CompletedTask;
}
public LocalizedString GetOrNull(string resourceName, string cultureName, string name)
{
return null;
}
public Task<IEnumerable<string>> GetSupportedCulturesAsync()
{
IEnumerable<string> emptyCultures = new string[0];
return Task.FromResult(emptyCultures);
}
}

35
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/NoneLocalizationPersistenceWriter.cs

@ -1,35 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Localization.Persistence;
[Dependency(TryRegister = true)]
public class NoneLocalizationPersistenceWriter : ILocalizationPersistenceWriter, ISingletonDependency
{
public Task<IEnumerable<string>> GetExistsTextsAsync(
string resourceName,
string cultureName,
IEnumerable<string> keys,
CancellationToken cancellationToken = default)
{
return Task.FromResult(keys);
}
public Task<bool> WriteLanguageAsync(LanguageInfo language, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
public Task<bool> WriteResourceAsync(LocalizationResourceBase resource, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
public Task<bool> WriteTextsAsync(IEnumerable<LocalizableStringText> texts, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
}

37
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/StaticLocalizationSaverHostService.cs

@ -1,37 +0,0 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Localization.Persistence;
public class StaticLocalizationSaverHostService : BackgroundService
{
private readonly AbpLocalizationPersistenceOptions _options;
private readonly IStaticLocalizationSaver _staticLocalizationSaver;
public StaticLocalizationSaverHostService(
IOptions<AbpLocalizationPersistenceOptions> options,
IStaticLocalizationSaver staticLocalizationSaver)
{
_options = options.Value;
_staticLocalizationSaver = staticLocalizationSaver;
}
protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
if (_options.SaveStaticLocalizationsToPersistence)
{
try
{
await _staticLocalizationSaver.SaveAsync();
}
catch (OperationCanceledException)
{
// Ignore
return;
}
}
}
}

129
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/README.EN.md

@ -1,129 +0,0 @@
# LINGYUN.Abp.Localization.Persistence
## Module Description
Localization component persistence module, providing functionality to persist localization resources to storage facilities. This module allows you to save static localization documents to persistent storage for easier management and maintenance.
## Features
* Support persisting static localization resources to storage facilities
* Provide read and write interfaces for localization resources
* Support custom persistence storage implementation
* Support asynchronous read and write operations
* Support multi-language culture support
* Support selective persistence of specified resources
## Installation
```bash
dotnet add package LINGYUN.Abp.Localization.Persistence
```
## Base Modules
* Volo.Abp.Localization
## Configuration
The module provides the following configuration options:
* SaveStaticLocalizationsToPersistence: Whether to enable localization resource persistence (default: true)
* SaveToPersistenceResources: List of resources to be persisted
## Usage
1. Add module dependency:
```csharp
[DependsOn(
typeof(AbpLocalizationPersistenceModule))]
public class YouProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpLocalizationPersistenceOptions>(options =>
{
// Enable persistence facility
options.SaveStaticLocalizationsToPersistence = true;
// Specify your localization resource type, static documents under this type will be persisted to storage facilities
options.AddPersistenceResource<YouProjectResource>();
});
// Or use extension method to persist localization resource type
Configure<AbpLocalizationOptions>(options =>
{
// Same effect as above
options.UsePersistence<YouProjectResource>();
});
}
}
```
## Extension Interfaces
### ILocalizationPersistenceReader
Used to read localization resources from persistent storage:
```csharp
public interface ILocalizationPersistenceReader
{
// Get localized string for specified resource
LocalizedString GetOrNull(string resourceName, string cultureName, string name);
// Fill localization dictionary
void Fill(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary);
// Asynchronously fill localization dictionary
Task FillAsync(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary);
// Get supported cultures list
Task<IEnumerable<string>> GetSupportedCulturesAsync();
}
```
### ILocalizationPersistenceWriter
Used to write localization resources to persistent storage:
```csharp
public interface ILocalizationPersistenceWriter
{
// Write language information
Task<bool> WriteLanguageAsync(LanguageInfo language);
// Write resource information
Task<bool> WriteResourceAsync(LocalizationResourceBase resource);
// Get existing texts
Task<IEnumerable<string>> GetExistsTextsAsync(
string resourceName,
string cultureName,
IEnumerable<string> keys);
// Write localization texts
Task<bool> WriteTextsAsync(IEnumerable<LocalizableStringText> texts);
}
```
## Custom Persistence Implementation
To implement custom persistence storage, you need to:
1. Implement `ILocalizationPersistenceReader` interface
2. Implement `ILocalizationPersistenceWriter` interface
3. Register your implementation in the module:
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<ILocalizationPersistenceReader, YourCustomReader>();
context.Services.AddTransient<ILocalizationPersistenceWriter, YourCustomWriter>();
}
```
## More Information
* [中文文档](./README.md)
* [ABP Localization Documentation](https://docs.abp.io/en/abp/latest/Localization)

129
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/README.md

@ -1,129 +0,0 @@
# LINGYUN.Abp.Localization.Persistence
## 模块说明
本地化组件持久层模块,提供将本地化资源持久化到存储设施的功能。此模块允许你将静态本地化文档保存到持久化存储中,方便管理和维护。
## 功能特性
* 支持将静态本地化资源持久化到存储设施
* 提供本地化资源的读写接口
* 支持自定义持久化存储实现
* 支持异步读写操作
* 支持多语言文化支持
* 支持选择性持久化指定的资源
## 安装
```bash
dotnet add package LINGYUN.Abp.Localization.Persistence
```
## 基础模块
* Volo.Abp.Localization
## 配置说明
模块提供以下配置选项:
* SaveStaticLocalizationsToPersistence:是否启用本地化资源持久化(默认:true)
* SaveToPersistenceResources:需要持久化的资源列表
## 使用方法
1. 添加模块依赖:
```csharp
[DependsOn(
typeof(AbpLocalizationPersistenceModule))]
public class YouProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpLocalizationPersistenceOptions>(options =>
{
// 启用持久化设施
options.SaveStaticLocalizationsToPersistence = true;
// 指定你的本地化资源类型, 此类型下定义的静态文档将被持久化到存储设施
options.AddPersistenceResource<YouProjectResource>();
});
// 或者使用扩展方法持久化本地化资源类型
Configure<AbpLocalizationOptions>(options =>
{
// 效果如上
options.UsePersistence<YouProjectResource>();
});
}
}
```
## 扩展接口
### ILocalizationPersistenceReader
用于从持久化存储中读取本地化资源:
```csharp
public interface ILocalizationPersistenceReader
{
// 获取指定资源的本地化字符串
LocalizedString GetOrNull(string resourceName, string cultureName, string name);
// 填充本地化字典
void Fill(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary);
// 异步填充本地化字典
Task FillAsync(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary);
// 获取支持的文化列表
Task<IEnumerable<string>> GetSupportedCulturesAsync();
}
```
### ILocalizationPersistenceWriter
用于将本地化资源写入持久化存储:
```csharp
public interface ILocalizationPersistenceWriter
{
// 写入语言信息
Task<bool> WriteLanguageAsync(LanguageInfo language);
// 写入资源信息
Task<bool> WriteResourceAsync(LocalizationResourceBase resource);
// 获取已存在的文本
Task<IEnumerable<string>> GetExistsTextsAsync(
string resourceName,
string cultureName,
IEnumerable<string> keys);
// 写入本地化文本
Task<bool> WriteTextsAsync(IEnumerable<LocalizableStringText> texts);
}
```
## 自定义持久化实现
要实现自定义的持久化存储,需要:
1. 实现 `ILocalizationPersistenceReader` 接口
2. 实现 `ILocalizationPersistenceWriter` 接口
3. 在模块中注册你的实现:
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<ILocalizationPersistenceReader, YourCustomReader>();
context.Services.AddTransient<ILocalizationPersistenceWriter, YourCustomWriter>();
}
```
## 更多信息
* [English Documentation](./README.EN.md)
* [ABP本地化文档](https://docs.abp.io/zh-Hans/abp/latest/Localization)

44
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/Volo/Abp/Localization/AbpLocalizationOptionsExtensions.cs

@ -1,44 +0,0 @@
using LINGYUN.Abp.Localization.Persistence;
using System;
namespace Volo.Abp.Localization;
public static class AbpLocalizationOptionsExtensions
{
public static void UsePersistence<TResource>(
this AbpLocalizationOptions options)
{
options.Resources
.Get<TResource>()
.Contributors
.Add(new LocalizationSaveToPersistenceContributor());
}
public static void UsePersistence(
this AbpLocalizationOptions options,
Type localizationResourceType)
{
options.Resources
.Get(localizationResourceType)
.Contributors
.Add(new LocalizationSaveToPersistenceContributor());
}
public static void UsePersistences(
this AbpLocalizationOptions options,
params Type[] localizationResourceTypes)
{
foreach (var localizationResourceType in localizationResourceTypes)
{
options.UsePersistence(localizationResourceType);
}
}
public static void UseAllPersistence(
this AbpLocalizationOptions options)
{
foreach (var resource in options.Resources)
{
resource.Value.Contributors.Add(new LocalizationSaveToPersistenceContributor());
}
}
}

1
aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsEntityFrameworkCoreModule.cs

@ -13,7 +13,6 @@ using LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore;
using LINGYUN.Abp.WeChat;
using LINGYUN.Platform.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict.EntityFrameworkCore;

3
aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Components/ProfileManagementGroup/TwoFactor/Default.js

@ -9,8 +9,9 @@ $(function () {
})
.then(function () {
abp.notify.success(ul("SavedSuccessfully"));
abp.ui.clearBusy();
})
.done(function () {
.catch(function () {
abp.ui.clearBusy();
});
});

74
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/AbpAuditingWebModule.cs

@ -1,74 +0,0 @@
using LINGYUN.Abp.Auditing.Web.ProfileManagement;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Account.Web;
using Volo.Abp.Account.Web.Pages.Account;
using Volo.Abp.Account.Web.ProfileManagement;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AuditLogging.Localization;
using Volo.Abp.AutoMapper;
using Volo.Abp.Http.ProxyScripting.Generators.JQuery;
using Volo.Abp.Modularity;
using Volo.Abp.UI.Navigation;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.Auditing.Web;
[DependsOn(
typeof(AbpAccountWebModule),
typeof(AbpAuditingApplicationContractsModule))]
public class AbpAuditingWebModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
{
options.AddAssemblyResource(typeof(AuditLoggingResource), typeof(AbpAuditingWebModule).Assembly);
});
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAuditingWebModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpAuditingWebModule>();
});
ConfigureProfileManagementPage();
context.Services.AddAutoMapperObjectMapper<AbpAuditingWebModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<AbpAuditingWebModule>(validate: true);
});
Configure<DynamicJavaScriptProxyOptions>(options =>
{
options.DisableModule(AuditingRemoteServiceConsts.ModuleName);
});
}
private void ConfigureProfileManagementPage()
{
Configure<ProfileManagementPageOptions>(options =>
{
options.Contributors.Add(new SecurityLogManagementPageContributor());
});
Configure<AbpBundlingOptions>(options =>
{
options.ScriptBundles
.Configure(typeof(ManageModel).FullName,
configuration =>
{
configuration.AddFiles("/client-proxies/auditing-proxy.js");
configuration.AddFiles("/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.js");
});
});
}
}

3
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/FodyWeavers.xml

@ -1,3 +0,0 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

39
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/LINGYUN.Abp.Auditing.Web.csproj

@ -1,39 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AssemblyName>LINGYUN.Abp.Auditing.Web</AssemblyName>
<PackageId>LINGYUN.Abp.Auditing.Web</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace>LINGYUN.Abp.Auditing.Web</RootNamespace>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Modules\**\*.js" />
<EmbeddedResource Include="Modules\**\*.css" />
<EmbeddedResource Include="Pages\**\*.js" />
<EmbeddedResource Include="Pages\**\*.css" />
<EmbeddedResource Include="wwwroot\**\*.js" />
<EmbeddedResource Include="wwwroot\**\*.css" />
<Content Remove="Modules\**\*.js" />
<Content Remove="Modules\**\*.css" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Account.Web" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Auditing.Application.Contracts\LINGYUN.Abp.Auditing.Application.Contracts.csproj" />
</ItemGroup>
</Project>

19
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/Pages/Account/Components/ProfileManagementGroup/SecurityLog/AccountProfileSecurityLogManagementGroupViewComponent.cs

@ -1,19 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
namespace LINGYUN.Abp.Auditing.Web.Pages.Account.Components.ProfileManagementGroup.SecurityLog;
public class AccountProfileSecurityLogManagementGroupViewComponent : AbpViewComponent
{
public AccountProfileSecurityLogManagementGroupViewComponent()
{
}
public async virtual Task<IViewComponentResult> InvokeAsync()
{
await Task.CompletedTask;
return View("~/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.cshtml");
}
}

22
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.cshtml

@ -1,22 +0,0 @@
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@using Volo.Abp.Localization
@inject IHtmlLocalizer<AccountResource> L
@section scripts {
<abp-script src="/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.js" />
}
<abp-card>
<abp-card-header>
<abp-row>
<abp-column size-md="_6">
<abp-card-title>@L["SecurityLog"]</abp-card-title>
</abp-column>
</abp-row>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="SecurityLogTable"></abp-table>
</abp-card-body>
</abp-card>

60
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.js

@ -1,60 +0,0 @@
$(function () {
var ul = abp.localization.getResource('AbpUi');
var il = abp.localization.getResource('AbpIdentity');
var dataTable = $('#SecurityLogTable').DataTable(
abp.libs.datatables.normalizeConfiguration({
serverSide: true,
paging: true,
searching: false,
scrollX: true,
ajax: abp.libs.datatables.createAjax(labp.auditing.securityLogs.securityLog.getList),
columnDefs: [
{
title: ul('Actions'),
rowAction: {
items:
[
{
text: il('RevokeSession'),
confirmMessage: function () {
return il('SessionWillBeRevokedMessage');
},
visible: function (data) {
return data.sessionId !== abp.currentUser?.sessionId;
},
action: function (data) {
labp.account.myProfile
.revokeSession(data.record.sessionId)
.then(function () {
abp.notify.info(
il('SuccessfullyRevoked')
);
dataTable.ajax.reload();
});
}
}
]
}
},
{
title: il('DisplayName:Device'),
data: "device"
},
{
title: il('DisplayName:IpAddresses'),
data: "ipAddresses"
},
{
title: il('DisplayName:SignedIn'),
data: "signedIn",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString(luxon.DateTime.DATETIME_FULL_WITH_SECONDS);
}
}]
})
);
});

26
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/ProfileManagement/SecurityLogManagementPageContributor.cs

@ -1,26 +0,0 @@
using LINGYUN.Abp.Auditing.Web.Pages.Account.Components.ProfileManagementGroup.SecurityLog;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System.Threading.Tasks;
using Volo.Abp.Account.Web.ProfileManagement;
using Volo.Abp.AuditLogging.Localization;
namespace LINGYUN.Abp.Auditing.Web.ProfileManagement;
public class SecurityLogManagementPageContributor : IProfileManagementPageContributor
{
public virtual Task ConfigureAsync(ProfileManagementPageCreationContext context)
{
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<AuditLoggingResource>>();
context.Groups.Add(
new ProfileManagementPageGroup(
"LINGYUN.Abp.Account.SecurityLog",
l["SecurityLog"],
typeof(AccountProfileSecurityLogManagementGroupViewComponent)
)
);
return Task.CompletedTask;
}
}

12
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/Properties/launchSettings.json

@ -1,12 +0,0 @@
{
"profiles": {
"LINGYUN.Abp.Auditing.Web": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:62925;http://localhost:62926"
}
}
}

146
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Web/wwwroot/client-proxies/auditing-proxy.js

@ -1,146 +0,0 @@
/* This file is automatically generated by ABP framework to use MVC Controllers from javascript. */
// module auditing
(function(){
// controller labp.auditing.auditLogs.auditLog
(function(){
abp.utils.createNamespace(window, 'labp.auditing.auditLogs.auditLog');
labp.auditing.auditLogs.auditLog['delete'] = function(id, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/audit-log/' + id + '',
type: 'DELETE',
dataType: null
}, ajaxParams));
};
labp.auditing.auditLogs.auditLog.deleteMany = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/audit-log/bulk',
type: 'DELETE',
dataType: null,
data: JSON.stringify(input)
}, ajaxParams));
};
labp.auditing.auditLogs.auditLog.get = function(id, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/audit-log/' + id + '',
type: 'GET'
}, ajaxParams));
};
labp.auditing.auditLogs.auditLog.getList = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/audit-log' + abp.utils.buildQueryString([{ name: 'startTime', value: input.startTime }, { name: 'endTime', value: input.endTime }, { name: 'httpMethod', value: input.httpMethod }, { name: 'url', value: input.url }, { name: 'userId', value: input.userId }, { name: 'userName', value: input.userName }, { name: 'applicationName', value: input.applicationName }, { name: 'correlationId', value: input.correlationId }, { name: 'clientId', value: input.clientId }, { name: 'clientIpAddress', value: input.clientIpAddress }, { name: 'maxExecutionDuration', value: input.maxExecutionDuration }, { name: 'minExecutionDuration', value: input.minExecutionDuration }, { name: 'hasException', value: input.hasException }, { name: 'httpStatusCode', value: input.httpStatusCode }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
// controller labp.auditing.auditLogs.entityChanges
(function(){
abp.utils.createNamespace(window, 'labp.auditing.auditLogs.entityChanges');
labp.auditing.auditLogs.entityChanges.get = function(id, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/entity-changes/' + id + '',
type: 'GET'
}, ajaxParams));
};
labp.auditing.auditLogs.entityChanges.getList = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/entity-changes' + abp.utils.buildQueryString([{ name: 'auditLogId', value: input.auditLogId }, { name: 'startTime', value: input.startTime }, { name: 'endTime', value: input.endTime }, { name: 'changeType', value: input.changeType }, { name: 'entityId', value: input.entityId }, { name: 'entityTypeFullName', value: input.entityTypeFullName }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
type: 'GET'
}, ajaxParams));
};
labp.auditing.auditLogs.entityChanges.getWithUsernameAsyncById = function(id, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/entity-changes/with-username/' + id + '',
type: 'GET'
}, ajaxParams));
};
labp.auditing.auditLogs.entityChanges.getWithUsernameAsyncByInput = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/entity-changes/with-username' + abp.utils.buildQueryString([{ name: 'entityId', value: input.entityId }, { name: 'entityTypeFullName', value: input.entityTypeFullName }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
// controller labp.auditing.logging.log
(function(){
abp.utils.createNamespace(window, 'labp.auditing.logging.log');
labp.auditing.logging.log.get = function(id, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/logging/' + id + '',
type: 'GET'
}, ajaxParams));
};
labp.auditing.logging.log.getList = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/logging' + abp.utils.buildQueryString([{ name: 'startTime', value: input.startTime }, { name: 'endTime', value: input.endTime }, { name: 'level', value: input.level }, { name: 'machineName', value: input.machineName }, { name: 'environment', value: input.environment }, { name: 'application', value: input.application }, { name: 'context', value: input.context }, { name: 'requestId', value: input.requestId }, { name: 'requestPath', value: input.requestPath }, { name: 'correlationId', value: input.correlationId }, { name: 'processId', value: input.processId }, { name: 'threadId', value: input.threadId }, { name: 'hasException', value: input.hasException }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
// controller labp.auditing.securityLogs.securityLog
(function(){
abp.utils.createNamespace(window, 'labp.auditing.securityLogs.securityLog');
labp.auditing.securityLogs.securityLog['delete'] = function(id, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/security-log/' + id + '',
type: 'DELETE',
dataType: null
}, ajaxParams));
};
labp.auditing.securityLogs.securityLog.deleteMany = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/security-log/bulk',
type: 'DELETE',
dataType: null,
data: JSON.stringify(input)
}, ajaxParams));
};
labp.auditing.securityLogs.securityLog.get = function(id, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/security-log/' + id + '',
type: 'GET'
}, ajaxParams));
};
labp.auditing.securityLogs.securityLog.getList = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/auditing/security-log' + abp.utils.buildQueryString([{ name: 'startTime', value: input.startTime }, { name: 'endTime', value: input.endTime }, { name: 'applicationName', value: input.applicationName }, { name: 'identity', value: input.identity }, { name: 'actionName', value: input.actionName }, { name: 'userId', value: input.userId }, { name: 'userName', value: input.userName }, { name: 'clientId', value: input.clientId }, { name: 'correlationId', value: input.correlationId }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
})();

2
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN.Abp.LocalizationManagement.Domain.csproj

@ -14,12 +14,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Polly" />
<PackageReference Include="Volo.Abp.AutoMapper" />
<PackageReference Include="Volo.Abp.Ddd.Domain" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\localization\LINGYUN.Abp.Localization.Persistence\LINGYUN.Abp.Localization.Persistence.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.LocalizationManagement.Domain.Shared\LINGYUN.Abp.LocalizationManagement.Domain.Shared.csproj" />
</ItemGroup>

104
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementDomainModule.cs

@ -1,19 +1,29 @@
using LINGYUN.Abp.Localization.Persistence;
using LINGYUN.Abp.LocalizationManagement.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Polly;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.AutoMapper;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.LocalizationManagement;
[DependsOn(
typeof(AbpAutoMapperModule),
typeof(AbpDddDomainModule),
typeof(AbpLocalizationPersistenceModule),
typeof(AbpLocalizationManagementDomainSharedModule))]
public class AbpLocalizationManagementDomainModule : AbpModule
{
private readonly CancellationTokenSource cancellationTokenSource = new();
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<AbpLocalizationManagementDomainModule>();
@ -23,16 +33,86 @@ public class AbpLocalizationManagementDomainModule : AbpModule
options.AddProfile<LocalizationManagementDomainMapperProfile>(validate: true);
});
Configure<AbpLocalizationPersistenceOptions>(options =>
Configure<AbpLocalizationOptions>(options =>
{
options.AddPersistenceResource<LocalizationManagementResource>();
options.GlobalContributors.Add<LocalizationResourceContributor>();
});
// 分布式事件
//Configure<AbpDistributedEntityEventOptions>(options =>
//{
// options.AutoEventSelectors.Add<Text>();
// options.EtoMappings.Add<Text, TextEto>();
//});
Configure<AbpDistributedEntityEventOptions>(options =>
{
options.EtoMappings.Add<Text, TextEto>();
options.EtoMappings.Add<Language, LanguageEto>();
options.EtoMappings.Add<Resource, ResourceEto>();
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context));
}
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
await SaveLocalizationAsync(context);
}
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
{
cancellationTokenSource.CancelAsync();
return Task.CompletedTask;
}
private async Task SaveLocalizationAsync(ApplicationInitializationContext context)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpLocalizationManagementOptions>>();
if (options.Value.SaveStaticLocalizationsToDatabase)
{
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
await Task.Run(async () =>
{
using (var scope = rootServiceProvider.CreateScope())
{
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
var cancellationToken = applicationLifetime?.ApplicationStopping ?? cancellationTokenSource.Token;
try
{
using (cancellationTokenProvider.Use(cancellationToken))
{
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await Policy.Handle<Exception>()
.WaitAndRetryAsync(8,
retryAttempt => TimeSpan.FromSeconds(
RandomHelper.GetRandom((int)Math.Pow(2.0, retryAttempt) * 8, (int)Math.Pow(2.0, retryAttempt) * 12)))
.ExecuteAsync(async _ =>
{
try
{
await scope.ServiceProvider
.GetRequiredService<IStaticLocalizationSaver>()
.SaveAsync();
}
catch (Exception ex)
{
scope.ServiceProvider
.GetService<ILogger<AbpLocalizationModule>>()
?.LogException(ex);
throw;
}
},
cancellationTokenProvider.Token);
}
}
catch
{
}
}
});
}
}
}

17
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementOptions.cs

@ -1,24 +1,13 @@
using System;
namespace LINGYUN.Abp.LocalizationManagement;
namespace LINGYUN.Abp.LocalizationManagement;
public class AbpLocalizationManagementOptions
{
/// <summary>
/// 申请时间戳超时时间
/// default: 2 minutes
/// </summary>
public TimeSpan LocalizationCacheStampTimeOut { get; set; }
/// <summary>
/// 时间戳过期时间
/// default: 30 minutes
/// 保存本地化文本到数据库
/// </summary>
public TimeSpan LocalizationCacheStampExpiration { get; set; }
public bool SaveStaticLocalizationsToDatabase { get; set; }
public AbpLocalizationManagementOptions()
{
LocalizationCacheStampTimeOut = TimeSpan.FromMinutes(2);
// 30分钟过期重新刷新缓存
LocalizationCacheStampExpiration = TimeSpan.FromMinutes(30);
}
}

10
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationLanguageStoreCache.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
public interface ILocalizationLanguageStoreCache
{
Task<IReadOnlyList<LanguageInfo>> GetLanguagesAsync();
}

29
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationStoreCache.cs

@ -1,29 +0,0 @@
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
public interface ILocalizationStoreCache
{
string CacheStamp { get; set; }
SemaphoreSlim SyncSemaphore { get; }
DateTime? LastCheckTime { get; set; }
Task InitializeAsync(LocalizationStoreCacheInitializeContext context);
LocalizationResourceBase GetResourceOrNull(string resourceName);
LocalizedString GetLocalizedStringOrNull(string resourceName, string cultureName, string name);
IReadOnlyList<LocalizationResourceBase> GetResources();
IReadOnlyList<LanguageInfo> GetLanguages();
IDictionary<string, LocalizationDictionary> GetAllLocalizedStrings(string cultureName);
}

15
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationTextStoreCache.cs

@ -0,0 +1,15 @@
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
public interface ILocalizationTextStoreCache
{
LocalizedString GetOrNull(LocalizationResourceBase resource, string cultureName, string name);
void Fill(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary);
Task FillAsync(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary);
}

2
aspnet-core/framework/localization/LINGYUN.Abp.Localization.Persistence/LINGYUN/Abp/Localization/Persistence/IStaticLocalizationSaver.cs → aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/IStaticLocalizationSaver.cs

@ -1,6 +1,6 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.Localization.Persistence;
namespace LINGYUN.Abp.LocalizationManagement;
public interface IStaticLocalizationSaver
{

4
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ITextRepository.cs

@ -20,6 +20,10 @@ namespace LINGYUN.Abp.LocalizationManagement
CancellationToken cancellationToken = default
);
List<Text> GetList(
string resourceName = null,
string cultureName = null);
Task<List<Text>> GetListAsync(
string resourceName = null,
string cultureName = null,

124
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationCacheInvalidator.cs

@ -1,124 +0,0 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationCacheInvalidator :
ILocalEventHandler<EntityChangedEventData<Language>>,
ILocalEventHandler<EntityChangedEventData<Resource>>,
ILocalEventHandler<EntityChangedEventData<Text>>,
IDistributedEventHandler<EntityCreatedEto<TextEto>>,
IDistributedEventHandler<EntityUpdatedEto<TextEto>>,
IDistributedEventHandler<EntityDeletedEto<TextEto>>,
IDistributedEventHandler<EntityCreatedEto<ResourceEto>>,
IDistributedEventHandler<EntityUpdatedEto<ResourceEto>>,
IDistributedEventHandler<EntityDeletedEto<ResourceEto>>,
IDistributedEventHandler<EntityCreatedEto<LanguageEto>>,
IDistributedEventHandler<EntityUpdatedEto<LanguageEto>>,
IDistributedEventHandler<EntityDeletedEto<LanguageEto>>,
ITransientDependency
{
private readonly IClock _clock;
private readonly IDistributedCache _distributedCache;
private readonly ILocalizationStoreCache _storeCache;
private readonly AbpDistributedCacheOptions _distributedCacheOptions;
public LocalizationCacheInvalidator(
IClock clock,
ILocalizationStoreCache storeCache,
IDistributedCache distributedCache,
IOptions<AbpDistributedCacheOptions> options)
{
_clock = clock;
_storeCache = storeCache;
_distributedCache = distributedCache;
_distributedCacheOptions = options.Value;
}
public async virtual Task HandleEventAsync(EntityChangedEventData<Language> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityChangedEventData<Resource> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityChangedEventData<Text> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityCreatedEto<TextEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityUpdatedEto<TextEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityDeletedEto<TextEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityCreatedEto<ResourceEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityDeletedEto<ResourceEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityCreatedEto<LanguageEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityUpdatedEto<LanguageEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityDeletedEto<LanguageEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
public async virtual Task HandleEventAsync(EntityUpdatedEto<ResourceEto> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
protected async virtual Task RemoveStampInDistributedCacheAsync()
{
using (await _storeCache.SyncSemaphore.LockAsync())
{
var cacheKey = $"{_distributedCacheOptions.KeyPrefix}_AbpInMemoryLocalizationCacheStamp";
await _distributedCache.RemoveAsync(cacheKey);
_storeCache.CacheStamp = Guid.NewGuid().ToString();
_storeCache.LastCheckTime = _clock.Now.AddMinutes(-5);
}
}
}

8
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionary.cs

@ -1,8 +0,0 @@
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationDictionary : Dictionary<string, LocalizedString>
{
}

6
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithCulture.cs

@ -1,6 +0,0 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationDictionaryWithCulture : Dictionary<string, LocalizationDictionary>
{
}

6
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithResource.cs

@ -1,6 +0,0 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationDictionaryWithResource : Dictionary<string, LocalizationDictionaryWithCulture>
{
}

21
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageCacheInvalidator.cs

@ -0,0 +1,21 @@
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationLanguageCacheInvalidator: ILocalEventHandler<EntityChangedEventData<Language>>, ITransientDependency
{
private readonly IDistributedCache<LocalizationLanguageCacheItem> _localizationLanguageCache;
public LocalizationLanguageCacheInvalidator(IDistributedCache<LocalizationLanguageCacheItem> localizationLanguageCache)
{
_localizationLanguageCache = localizationLanguageCache;
}
public async virtual Task HandleEventAsync(EntityChangedEventData<Language> eventData)
{
await _localizationLanguageCache.RemoveAsync(LocalizationLanguageCacheItem.CacheKey);
}
}

24
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageCacheItem.cs

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.LocalizationManagement;
[Serializable]
[IgnoreMultiTenancy]
public class LocalizationLanguageCacheItem
{
internal const string CacheKey = "Abp.Localization.Languages";
public List<LanguageInfo> Languages { get; set; }
public LocalizationLanguageCacheItem()
{
}
public LocalizationLanguageCacheItem(List<LanguageInfo> languages)
{
Languages = languages;
}
}

7
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageDictionary.cs

@ -1,7 +0,0 @@
using System.Collections.Generic;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationLanguageDictionary : Dictionary<string, LanguageInfo>
{
}

21
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageProvider.cs

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
[Dependency(ReplaceServices = true)]
public class LocalizationLanguageProvider : ILanguageProvider, ITransientDependency
{
protected ILocalizationLanguageStoreCache LocalizationLanguageStore { get; }
public LocalizationLanguageProvider(ILocalizationLanguageStoreCache localizationLanguageStore)
{
LocalizationLanguageStore = localizationLanguageStore;
}
public async virtual Task<IReadOnlyList<LanguageInfo>> GetLanguagesAsync()
{
return await LocalizationLanguageStore.GetLanguagesAsync();
}
}

50
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageStoreCache.cs

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.ChangeTracking;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationLanguageStoreCache : ILocalizationLanguageStoreCache, ITransientDependency
{
protected IDistributedCache<LocalizationLanguageCacheItem> LanguageCache { get; }
protected ILanguageRepository LanguageRepository { get; }
public LocalizationLanguageStoreCache(
IDistributedCache<LocalizationLanguageCacheItem> languageCache,
ILanguageRepository languageRepository)
{
LanguageCache = languageCache;
LanguageRepository = languageRepository;
}
[DisableEntityChangeTracking]
public async virtual Task<IReadOnlyList<LanguageInfo>> GetLanguagesAsync()
{
var cacheItem = await GetCacheItemAsync();
return cacheItem.Languages.ToImmutableList();
}
protected async virtual Task<LocalizationLanguageCacheItem> GetCacheItemAsync()
{
var cacheItem = await LanguageCache.GetAsync(LocalizationLanguageCacheItem.CacheKey);
if (cacheItem != null)
{
return cacheItem;
}
var languages = await LanguageRepository.GetListAsync();
cacheItem = new LocalizationLanguageCacheItem(
languages.Select(x =>
new LanguageInfo(x.CultureName, x.UiCultureName, x.DisplayName))
.ToList());
return cacheItem;
}
}

65
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementPersistenceReader.cs

@ -1,65 +0,0 @@
using LINGYUN.Abp.Localization.Persistence;
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.LocalizationManagement;
[Dependency(ReplaceServices = true)]
[ExposeServices(
typeof(ILocalizationPersistenceReader),
typeof(LocalizationManagementPersistenceReader))]
public class LocalizationManagementPersistenceReader : ILocalizationPersistenceReader, ITransientDependency
{
private readonly ILocalizationStoreCache _localizationStoreCache;
private readonly LocalizationStoreCacheInitializeContext _cacheInitializeContext;
public LocalizationManagementPersistenceReader(
IServiceProvider serviceProvider,
ILocalizationStoreCache localizationStoreCache)
{
_localizationStoreCache = localizationStoreCache;
_cacheInitializeContext = new LocalizationStoreCacheInitializeContext(serviceProvider);
}
public virtual void Fill(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
AsyncHelper.RunSync(async () => await FillAsync(resourceName, cultureName, dictionary));
}
public async virtual Task FillAsync(string resourceName, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
await _localizationStoreCache.InitializeAsync(_cacheInitializeContext);
var localizedStrings = _localizationStoreCache.GetAllLocalizedStrings(cultureName);
var localizedStringsInResource = localizedStrings.GetOrDefault(resourceName);
if (localizedStringsInResource != null)
{
foreach (var localizedString in localizedStringsInResource)
{
dictionary[localizedString.Key] = localizedString.Value;
}
}
}
public virtual LocalizedString GetOrNull(string resourceName, string cultureName, string name)
{
return _localizationStoreCache.GetLocalizedStringOrNull(resourceName, cultureName, name);
}
public virtual Task<IEnumerable<string>> GetSupportedCulturesAsync()
{
var languageInfos = _localizationStoreCache.GetLanguages();
IEnumerable<string> languages = languageInfos
.Select(x => x.CultureName)
.ToList();
return Task.FromResult(languages);
}
}

203
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementPersistenceWriter.cs

@ -1,203 +0,0 @@
using LINGYUN.Abp.Localization.Persistence;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Guids;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
[Dependency(ReplaceServices = true)]
[ExposeServices(
typeof(ILocalizationPersistenceWriter),
typeof(LocalizationManagementPersistenceWriter))]
public class LocalizationManagementPersistenceWriter : ILocalizationPersistenceWriter, ITransientDependency
{
protected IDistributedCache Cache { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
protected IApplicationInfoAccessor ApplicationInfoAccessor { get; }
protected IGuidGenerator GuidGenerator { get; }
protected ILanguageRepository LanguageRepository { get; }
protected ITextRepository TextRepository { get; }
protected IResourceRepository ResourceRepository { get; }
public LocalizationManagementPersistenceWriter(
IDistributedCache cache,
IGuidGenerator guidGenerator,
IAbpDistributedLock distributedLock,
ITextRepository textRepository,
ILanguageRepository languageRepository,
IResourceRepository resourceRepository,
IApplicationInfoAccessor applicationInfoAccessor,
IOptions<AbpDistributedCacheOptions> cacheOptions)
{
Cache = cache;
GuidGenerator = guidGenerator;
DistributedLock = distributedLock;
LanguageRepository = languageRepository;
TextRepository = textRepository;
ResourceRepository = resourceRepository;
ApplicationInfoAccessor = applicationInfoAccessor;
CacheOptions = cacheOptions.Value;
}
public async virtual Task<IEnumerable<string>> GetExistsTextsAsync(
string resourceName,
string cultureName,
IEnumerable<string> keys,
CancellationToken cancellationToken = default)
{
if (!await ShouldCalculateTextsHash(keys, cancellationToken))
{
return keys;
}
return await TextRepository.GetExistsKeysAsync(
resourceName,
cultureName,
keys,
cancellationToken);
}
public async virtual Task<bool> WriteLanguageAsync(
LanguageInfo language,
CancellationToken cancellationToken = default)
{
var commonDistributedLockKey = GetCommonDistributedLockKey("Language", language.CultureName);
await using var lockHandle = await DistributedLock.TryAcquireAsync(commonDistributedLockKey);
if (lockHandle == null)
{
return false;
}
if (await LanguageRepository.FindByCultureNameAsync(language.CultureName, cancellationToken) == null)
{
await LanguageRepository.InsertAsync(
new Language(
GuidGenerator.Create(),
language.CultureName,
language.UiCultureName,
language.DisplayName,
language.TwoLetterISOLanguageName),
autoSave: true,
cancellationToken: cancellationToken);
}
return true;
}
public async virtual Task<bool> WriteResourceAsync(
LocalizationResourceBase resource,
CancellationToken cancellationToken = default)
{
var commonDistributedLockKey = GetCommonDistributedLockKey("Resource", resource.ResourceName);
await using var lockHandle = await DistributedLock.TryAcquireAsync(commonDistributedLockKey);
if (lockHandle == null)
{
return false;
}
if (await ResourceRepository.FindByNameAsync(resource.ResourceName, cancellationToken) == null)
{
await ResourceRepository.InsertAsync(
new Resource(
GuidGenerator.Create(),
resource.ResourceName,
resource.ResourceName,
resource.ResourceName,
resource.DefaultCultureName),
autoSave: true,
cancellationToken: cancellationToken);
}
return true;
}
public async virtual Task<bool> WriteTextsAsync(
IEnumerable<LocalizableStringText> texts,
CancellationToken cancellationToken = default)
{
if (!await ShouldCalculateTextsHash(texts.Select(x => x.Name), cancellationToken))
{
return false;
}
var cacheKey = GetApplicationHashCacheKey();
var currentHash = CalculateTextsHash(texts.Select(x => x.Name));
var savedTexts = texts.Select(text =>
new Text(
text.ResourceName,
text.CultureName,
text.Name,
text.Value));
await TextRepository.InsertManyAsync(
savedTexts,
autoSave: true,
cancellationToken: cancellationToken);
await Cache.SetStringAsync(
cacheKey,
currentHash,
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(30)
},
cancellationToken);
return true;
}
private async Task<bool> ShouldCalculateTextsHash(IEnumerable<string> texts, CancellationToken cancellationToken = default)
{
var cacheKey = GetApplicationHashCacheKey();
var cachedHash = await Cache.GetStringAsync(cacheKey, cancellationToken);
var currentHash = CalculateTextsHash(texts);
if (cachedHash == currentHash)
{
return false;
}
return cachedHash != currentHash;
}
private static string CalculateTextsHash(IEnumerable<string> texts)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("LocalizableStrings:");
stringBuilder.Append(texts.JoinAsString(","));
return stringBuilder
.ToString()
.ToMd5();
}
private string GetCommonDistributedLockKey(
string lockResourceName,
string lockResourceKey)
{
return $"{CacheOptions.KeyPrefix}_Common_AbpLocalizationWriter_{lockResourceName}_{lockResourceKey}_Lock";
}
private string GetApplicationHashCacheKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_AbpLocalizationHash";
}
}

45
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationResourceContributor.cs

@ -0,0 +1,45 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationResourceContributor : ILocalizationResourceContributor
{
public bool IsDynamic => true;
protected LocalizationResourceBase Resource { get; private set; }
protected ILocalizationTextStoreCache LocalizationTextStoreCache { get; private set; }
protected ILocalizationLanguageStoreCache LocalizationLanguageStoreCache { get; private set; }
public virtual void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
LocalizationTextStoreCache.Fill(Resource, cultureName, dictionary);
}
public async virtual Task FillAsync(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
await LocalizationTextStoreCache.FillAsync(Resource, cultureName, dictionary);
}
public virtual LocalizedString GetOrNull(string cultureName, string name)
{
return LocalizationTextStoreCache.GetOrNull(Resource, cultureName, name);
}
public async virtual Task<IEnumerable<string>> GetSupportedCulturesAsync()
{
var languageInfos = await LocalizationLanguageStoreCache.GetLanguagesAsync();
return languageInfos.Select(x => x.CultureName);
}
public void Initialize(LocalizationResourceInitializationContext context)
{
Resource = context.Resource;
LocalizationTextStoreCache = context.ServiceProvider.GetRequiredService<ILocalizationTextStoreCache>();
LocalizationLanguageStoreCache = context.ServiceProvider.GetRequiredService<ILocalizationLanguageStoreCache>();
}
}

160
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStore.cs

@ -1,160 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.Localization.External;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.LocalizationManagement;
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
[ExposeServices(
typeof(IExternalLocalizationStore),
typeof(LocalizationStore))]
public class LocalizationStore : IExternalLocalizationStore
{
protected IServiceProvider ServiceProvider { get; }
protected ILocalizationStoreCache LocalizationStoreCache { get; }
public LocalizationStore(
IServiceProvider serviceProvider,
ILocalizationStoreCache localizationStoreCache)
{
ServiceProvider = serviceProvider;
LocalizationStoreCache = localizationStoreCache;
}
[Obsolete("The framework already supports dynamic languages and will be deprecated in the next release")]
public async virtual Task<List<LanguageInfo>> GetLanguageListAsync(
CancellationToken cancellationToken = default)
{
var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
await LocalizationStoreCache.InitializeAsync(context);
return LocalizationStoreCache.GetLanguages().ToList();
}
[Obsolete("The framework already supports dynamic languages and will be deprecated in the next release")]
public async virtual Task<Dictionary<string, ILocalizationDictionary>> GetLocalizationDictionaryAsync(
string resourceName,
CancellationToken cancellationToken = default)
{
var dictionaries = new Dictionary<string, ILocalizationDictionary>();
var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
await LocalizationStoreCache.InitializeAsync(context);
var resource = LocalizationStoreCache.GetResourceOrNull(resourceName);
if (resource == null)
{
// 资源不存在或未启用返回空
return dictionaries;
}
var texts = LocalizationStoreCache.GetAllLocalizedStrings(CultureInfo.CurrentCulture.Name);
foreach (var textGroup in texts)
{
var cultureTextDictionaires = new Dictionary<string, LocalizedString>();
foreach (var text in textGroup.Value)
{
// 本地化名称去重
if (!cultureTextDictionaires.ContainsKey(text.Key))
{
cultureTextDictionaires[text.Key] = new LocalizedString(text.Key, text.Value.Value.NormalizeLineEndings());
}
}
// 本地化语言去重
if (!dictionaries.ContainsKey(textGroup.Key))
{
dictionaries[textGroup.Key] = new StaticLocalizationDictionary(textGroup.Key, cultureTextDictionaires);
}
}
return dictionaries;
}
[Obsolete("The framework already supports dynamic languages and will be deprecated in the next release")]
public async virtual Task<Dictionary<string, Dictionary<string, ILocalizationDictionary>>> GetAllLocalizationDictionaryAsync(CancellationToken cancellationToken = default)
{
var result = new Dictionary<string, Dictionary<string, ILocalizationDictionary>>();
var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
await LocalizationStoreCache.InitializeAsync(context);
var textList = LocalizationStoreCache.GetAllLocalizedStrings(CultureInfo.CurrentCulture.Name);
foreach (var resourcesGroup in textList)
{
var dictionaries = new Dictionary<string, ILocalizationDictionary>();
foreach (var text in resourcesGroup.Value)
{
var cultureTextDictionaires = new Dictionary<string, LocalizedString>();
// 本地化名称去重
if (!cultureTextDictionaires.ContainsKey(text.Key))
{
cultureTextDictionaires[text.Key] = new LocalizedString(text.Key, text.Value.Value.NormalizeLineEndings());
}
// 本地化语言去重
if (!dictionaries.ContainsKey(text.Key))
{
dictionaries[text.Key] = new StaticLocalizationDictionary(text.Key, cultureTextDictionaires);
}
}
result.Add(resourcesGroup.Key, dictionaries);
}
return result;
}
[Obsolete("The framework already supports dynamic languages and will be deprecated in the next release")]
public async virtual Task<bool> ResourceExistsAsync(string resourceName, CancellationToken cancellationToken = default)
{
var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
await LocalizationStoreCache.InitializeAsync(context);
return LocalizationStoreCache.GetResourceOrNull(resourceName) != null;
}
public LocalizationResourceBase GetResourceOrNull(string resourceName)
{
return AsyncHelper.RunSync(async () => await GetResourceOrNullAsync(resourceName));
}
public async virtual Task<LocalizationResourceBase> GetResourceOrNullAsync(string resourceName)
{
var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
await LocalizationStoreCache.InitializeAsync(context);
return LocalizationStoreCache.GetResourceOrNull(resourceName);
}
public async virtual Task<string[]> GetResourceNamesAsync()
{
var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
await LocalizationStoreCache.InitializeAsync(context);
return LocalizationStoreCache.GetResources()
.Select(x => x.ResourceName)
.ToArray();
}
public async virtual Task<LocalizationResourceBase[]> GetResourcesAsync()
{
var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
await LocalizationStoreCache.InitializeAsync(context);
return LocalizationStoreCache.GetResources().ToArray();
}
}

12
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreCacheInitializeContext.cs

@ -1,12 +0,0 @@
using System;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationStoreCacheInitializeContext
{
public IServiceProvider ServiceProvider { get; }
public LocalizationStoreCacheInitializeContext(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
}

243
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreInMemoryCache.cs

@ -1,243 +0,0 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Localization;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.LocalizationManagement;
[ExposeServices(
typeof(ILocalizationStoreCache),
typeof(LocalizationStoreInMemoryCache))]
public class LocalizationStoreInMemoryCache : ILocalizationStoreCache, ISingletonDependency
{
public string CacheStamp { get; set; }
public DateTime? LastCheckTime { get; set; }
public SemaphoreSlim SyncSemaphore { get; } = new(1, 1);
protected LocalizationResourceDictionary Resources { get; }
protected LocalizationLanguageDictionary Languages { get; }
protected LocalizationDictionaryWithResource LocalizedStrings { get; }
private readonly IClock _clock;
private readonly IDistributedCache _distributedCache;
private readonly IAbpDistributedLock _distributedLock;
private readonly AbpDistributedCacheOptions _distributedCacheOptions;
private readonly AbpLocalizationManagementOptions _managementOptions;
public LocalizationStoreInMemoryCache(
IClock clock,
IDistributedCache distributedCache,
IAbpDistributedLock distributedLock,
IOptions<AbpDistributedCacheOptions> distributedCacheOptions,
IOptions<AbpLocalizationManagementOptions> managementOptions)
{
_clock = clock;
_distributedCache = distributedCache;
_distributedLock = distributedLock;
_distributedCacheOptions = distributedCacheOptions.Value;
_managementOptions = managementOptions.Value;
Resources = new LocalizationResourceDictionary();
Languages = new LocalizationLanguageDictionary();
LocalizedStrings = new LocalizationDictionaryWithResource();
}
public async virtual Task InitializeAsync(LocalizationStoreCacheInitializeContext context)
{
using (await SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync(context);
}
}
public virtual IDictionary<string, LocalizationDictionary> GetAllLocalizedStrings(string cultureName)
{
var localizedStrings = new Dictionary<string, LocalizationDictionary>();
foreach (var resource in Resources)
{
var localizedStringsInResource = LocalizedStrings.GetOrDefault(resource.Key);
if (localizedStringsInResource == null)
{
continue;
}
var localizedStringsInCurrentCulture = localizedStringsInResource.GetOrDefault(cultureName);
if (localizedStringsInCurrentCulture == null)
{
continue;
}
var currentCultureLocalizedStrings = new LocalizationDictionary();
foreach (var localizedString in localizedStringsInCurrentCulture)
{
if (!currentCultureLocalizedStrings.ContainsKey(localizedString.Key))
{
currentCultureLocalizedStrings.Add(localizedString.Key, localizedString.Value);
}
}
localizedStrings[resource.Key] = currentCultureLocalizedStrings;
}
return localizedStrings;
}
public virtual LocalizedString GetLocalizedStringOrNull(string resourceName, string cultureName, string name)
{
var localizedStringsInResource = LocalizedStrings.GetOrDefault(resourceName);
if (localizedStringsInResource == null)
{
return null;
}
var currentCultureLocalizedStrings = localizedStringsInResource.GetOrDefault(cultureName);
if (currentCultureLocalizedStrings == null)
{
return null;
}
return currentCultureLocalizedStrings.GetOrDefault(name);
}
public virtual LocalizationResourceBase GetResourceOrNull(string resourceName)
{
return Resources.GetOrDefault(resourceName);
}
public virtual IReadOnlyList<LocalizationResourceBase> GetResources()
{
return Resources.Values.ToImmutableList();
}
public virtual IReadOnlyList<LanguageInfo> GetLanguages()
{
return Languages.Values.ToImmutableList();
}
protected async virtual Task EnsureCacheIsUptoDateAsync(LocalizationStoreCacheInitializeContext context)
{
if (LastCheckTime.HasValue &&
_clock.Now.Subtract(LastCheckTime.Value).TotalSeconds < 30)
{
return;
}
var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == CacheStamp)
{
LastCheckTime = _clock.Now;
return;
}
await UpdateInMemoryStoreCache(context);
CacheStamp = stampInDistributedCache;
LastCheckTime = _clock.Now;
}
protected async virtual Task UpdateInMemoryStoreCache(LocalizationStoreCacheInitializeContext context)
{
var textRepository = context.ServiceProvider.GetRequiredService<ITextRepository>();
var languageRepository = context.ServiceProvider.GetRequiredService<ILanguageRepository>();
var resourceRepository = context.ServiceProvider.GetRequiredService<IResourceRepository>();
var resourceRecords = await resourceRepository.GetListAsync();
var languageRecords = await languageRepository.GetActivedListAsync();
var textRecords = await textRepository.GetListAsync();
Resources.Clear();
Languages.Clear();
foreach (var resourceRecord in resourceRecords)
{
Resources[resourceRecord.Name] = new NonTypedLocalizationResource(resourceRecord.Name, resourceRecord.DefaultCultureName);
var localizedStrings = LocalizedStrings.GetOrDefault(resourceRecord.Name);
localizedStrings ??= new LocalizationDictionaryWithCulture();
localizedStrings.Clear();
// 需要按照不同文化聚合
foreach (var textRecordByCulture in textRecords.Where(x => x.ResourceName == resourceRecord.Name).GroupBy(x => x.CultureName))
{
var currentCultureLocalizedStrings = new LocalizationDictionary();
foreach (var textRecord in textRecordByCulture)
{
currentCultureLocalizedStrings[textRecord.Key] = new LocalizedString(textRecord.Key, textRecord.Value);
}
localizedStrings[textRecordByCulture.Key] = currentCultureLocalizedStrings;
}
LocalizedStrings[resourceRecord.Name] = localizedStrings;
}
foreach (var language in languageRecords)
{
Languages[language.CultureName] = new LanguageInfo(
language.CultureName,
language.UiCultureName,
language.DisplayName);
}
}
protected async virtual Task<string> GetOrSetStampInDistributedCache()
{
var cacheKey = $"{_distributedCacheOptions.KeyPrefix}_AbpInMemoryLocalizationCacheStamp";
var stampInDistributedCache = await _distributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
var distributedLockKey = $"{_distributedCacheOptions.KeyPrefix}_AbpLocalizationUpdateLock";
await using (var commonLockHandle = await _distributedLock
.TryAcquireAsync(distributedLockKey, _managementOptions.LocalizationCacheStampTimeOut))
{
if (commonLockHandle == null)
{
/* This request will fail */
throw new AbpException(
"Could not acquire distributed lock for localization stamp check!"
);
}
stampInDistributedCache = await _distributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
stampInDistributedCache = Guid.NewGuid().ToString();
await _distributedCache.SetStringAsync(
cacheKey,
stampInDistributedCache,
new DistributedCacheEntryOptions
{
SlidingExpiration = _managementOptions.LocalizationCacheStampExpiration
}
);
}
return stampInDistributedCache;
}
}

25
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationTextCacheInvalidator.cs

@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationTextCacheInvalidator : ILocalEventHandler<EntityChangedEventData<Text>>, ITransientDependency
{
private readonly IDistributedCache<LocalizationTextCacheItem> _localizationTextCache;
public LocalizationTextCacheInvalidator(IDistributedCache<LocalizationTextCacheItem> localizationTextCache)
{
_localizationTextCache = localizationTextCache;
}
public async virtual Task HandleEventAsync(EntityChangedEventData<Text> eventData)
{
var cacheKey = LocalizationTextCacheItem.CalculateCacheKey(
eventData.Entity.ResourceName,
eventData.Entity.CultureName);
await _localizationTextCache.RemoveAsync(cacheKey);
}
}

30
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationTextCacheItem.cs

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.LocalizationManagement;
[Serializable]
[IgnoreMultiTenancy]
public class LocalizationTextCacheItem
{
private const string CacheKeyFormat = "r:{0},c:{1}";
public string ResourceName { get; set; }
public string CultureName { get; set; }
public Dictionary<string, string> Texts { get; set; }
public LocalizationTextCacheItem()
{
Texts = new Dictionary<string, string>();
}
public LocalizationTextCacheItem(string resourceName, string cultureName, Dictionary<string, string> texts)
{
ResourceName = resourceName;
CultureName = cultureName;
Texts = texts;
}
public static string CalculateCacheKey(string resourceName, string cultureName)
{
return string.Format(CacheKeyFormat, resourceName, cultureName);
}
}

119
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationTextStoreCache.cs

@ -0,0 +1,119 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.LocalizationManagement;
public class LocalizationTextStoreCache : ILocalizationTextStoreCache, ISingletonDependency
{
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IDistributedCache<LocalizationTextCacheItem> LocalizationTextCache { get; }
public LocalizationTextStoreCache(
IServiceScopeFactory serviceScopeFactory,
IDistributedCache<LocalizationTextCacheItem> localizationTextCache)
{
ServiceScopeFactory = serviceScopeFactory;
LocalizationTextCache = localizationTextCache;
}
public virtual void Fill(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var cacheItem = GetCacheItem(resource, cultureName);
foreach (var text in cacheItem.Texts)
{
dictionary[text.Key] = new LocalizedString(text.Key, text.Value);
}
}
public async virtual Task FillAsync(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var cacheItem = await GetCacheItemAsync(resource, cultureName);
foreach (var text in cacheItem.Texts)
{
dictionary[text.Key] = new LocalizedString(text.Key, text.Value);
}
}
public virtual LocalizedString GetOrNull(LocalizationResourceBase resource, string cultureName, string name)
{
var cacheItem = GetCacheItem(resource, cultureName);
var value = cacheItem.Texts.GetOrDefault(name);
if (value.IsNullOrWhiteSpace())
{
return null;
}
return new LocalizedString(name, value);
}
protected virtual LocalizationTextCacheItem GetCacheItem(LocalizationResourceBase resource, string cultureName)
{
var cacheKey = LocalizationTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName);
var cacheItem = LocalizationTextCache.Get(cacheKey);
if (cacheItem != null)
{
return cacheItem;
}
var setTexts = new Dictionary<string, string>();
using (var scope = ServiceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider.GetRequiredService<IEntityChangeTrackingProvider>();
using (provider.Change(false))
{
var repo = scope.ServiceProvider.GetRequiredService<ITextRepository>();
var texts = repo.GetList(resource.ResourceName, cultureName);
foreach (var text in texts)
{
setTexts[text.Key] = text.Value;
}
}
}
cacheItem = new LocalizationTextCacheItem(resource.ResourceName, cultureName, setTexts);
LocalizationTextCache.Set(cacheKey, cacheItem);
return cacheItem;
}
protected async virtual Task<LocalizationTextCacheItem> GetCacheItemAsync(LocalizationResourceBase resource, string cultureName)
{
var cacheKey = LocalizationTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName);
var cacheItem = await LocalizationTextCache.GetAsync(cacheKey);
if (cacheItem != null)
{
return cacheItem;
}
var setTexts = new Dictionary<string, string>();
using (var scope = ServiceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider.GetRequiredService<IEntityChangeTrackingProvider>();
using (provider.Change(false))
{
var repo = scope.ServiceProvider.GetRequiredService<ITextRepository>();
var texts = await repo.GetListAsync(resource.ResourceName, cultureName);
foreach (var text in texts)
{
setTexts[text.Key] = text.Value;
}
}
}
cacheItem = new LocalizationTextCacheItem(resource.ResourceName, cultureName, setTexts);
await LocalizationTextCache.SetAsync(cacheKey, cacheItem);
return cacheItem;
}
}

309
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs

@ -0,0 +1,309 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Guids;
using Volo.Abp.Localization;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace LINGYUN.Abp.LocalizationManagement;
public class StaticLocalizationSaver : IStaticLocalizationSaver, ITransientDependency
{
protected ILogger<StaticLocalizationSaver> Logger { get; }
protected IDistributedCache Cache { get; }
protected IGuidGenerator GuidGenerator { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
protected IApplicationInfoAccessor ApplicationInfoAccessor { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IStringLocalizerFactory StringLocalizerFactory { get; }
protected AbpLocalizationOptions LocalizationOptions { get; }
protected AbpLocalizationManagementOptions LocalizationManagementOptions { get; }
protected ILanguageRepository LanguageRepository { get; }
protected IResourceRepository ResourceRepository { get; }
protected ITextRepository TextRepository { get; }
public StaticLocalizationSaver(
ILogger<StaticLocalizationSaver> logger,
IDistributedCache cache,
IGuidGenerator guidGenerator,
IAbpDistributedLock distributedLock,
IUnitOfWorkManager unitOfWorkManager,
IApplicationInfoAccessor applicationInfoAccessor,
ICancellationTokenProvider cancellationTokenProvider,
IStringLocalizerFactory stringLocalizerFactory,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IOptions<AbpLocalizationOptions> localizationOptions,
IOptions<AbpLocalizationManagementOptions> localizationManagementOptions,
ILanguageRepository languageRepository,
IResourceRepository resourceRepository,
ITextRepository textRepository)
{
Logger = logger;
Cache = cache;
GuidGenerator = guidGenerator;
DistributedLock = distributedLock;
UnitOfWorkManager = unitOfWorkManager;
CacheOptions = cacheOptions.Value;
ApplicationInfoAccessor = applicationInfoAccessor;
CancellationTokenProvider = cancellationTokenProvider;
StringLocalizerFactory = stringLocalizerFactory;
LocalizationOptions = localizationOptions.Value;
LocalizationManagementOptions = localizationManagementOptions.Value;
LanguageRepository = languageRepository;
ResourceRepository = resourceRepository;
TextRepository = textRepository;
}
public async virtual Task SaveAsync()
{
if (!LocalizationManagementOptions.SaveStaticLocalizationsToDatabase)
{
return;
}
Logger.LogDebug("Waiting to acquire the distributed lock for saving static localizations...");
await using var applicationLockHandle = await DistributedLock.TryAcquireAsync(GetApplicationDistributedLockKey());
if (applicationLockHandle == null)
{
return;
}
using var unitOfWork = UnitOfWorkManager.Begin(true, true);
try
{
await SaveLanguagesAsync();
await SaveResourcesAsync();
await SaveTextsAsync();
}
catch (Exception ex)
{
Logger.LogInformation("Filed to save static localizations.");
Logger.LogWarning(ex.ToString());
try
{
await unitOfWork.RollbackAsync();
}
catch
{
Logger.LogInformation("Filed to rollback saving static localizations.");
}
throw;
}
await unitOfWork.CompleteAsync();
Logger.LogInformation("Completed to save static localizations.");
}
private async Task SaveLanguagesAsync()
{
var languageHashKey = GetApplicationLanguageHashKey();
var languageHashCache = await Cache.GetStringAsync(languageHashKey, CancellationTokenProvider.Token);
var languageHash = CalculateLanguagesHash(LocalizationOptions.Languages);
if (languageHashCache != languageHash)
{
var newLanguages = new List<Language>();
foreach (var language in LocalizationOptions.Languages)
{
if (await LanguageRepository.FindByCultureNameAsync(language.CultureName, cancellationToken: CancellationTokenProvider.Token) == null)
{
newLanguages.Add(
new Language(
GuidGenerator.Create(),
language.CultureName,
language.UiCultureName,
language.DisplayName,
language.TwoLetterISOLanguageName));
}
}
if (newLanguages.Any())
{
Logger.LogInformation("Saved {0} new languages.", newLanguages.Count);
await LanguageRepository.InsertManyAsync(newLanguages, cancellationToken: CancellationTokenProvider.Token);
}
await Cache.SetStringAsync(languageHashKey, languageHash, CancellationTokenProvider.Token);
}
}
private async Task SaveResourcesAsync()
{
var resourceHashKey = GetApplicationResourceHashKey();
var resourceHashCache = await Cache.GetStringAsync(resourceHashKey, CancellationTokenProvider.Token);
var resourceHash = CalculateResourceHash(LocalizationOptions.Resources.Select(x => x.Value).ToList());
if (resourceHashCache != resourceHash)
{
var newResources = new List<Resource>();
foreach (var resource in LocalizationOptions.Resources)
{
if (await ResourceRepository.FindByNameAsync(resource.Key, cancellationToken: CancellationTokenProvider.Token) == null)
{
newResources.Add(
new Resource(
GuidGenerator.Create(),
resource.Value.ResourceName,
resource.Value.ResourceName,
resource.Value.ResourceName,
resource.Value.DefaultCultureName));
}
}
if (newResources.Any())
{
Logger.LogInformation("Saved {0} new resources.", newResources.Count);
await ResourceRepository.InsertManyAsync(newResources, cancellationToken: CancellationTokenProvider.Token);
}
await Cache.SetStringAsync(resourceHashKey, resourceHash, CancellationTokenProvider.Token);
}
}
private async Task SaveTextsAsync()
{
var createTexts = new List<Text>();
var updateTexts = new List<Text>();
var localizationResources = LocalizationOptions.Resources.Values.OfType<LocalizationResource>().ToArray();
var languageResourceTexts = new Dictionary<string, Dictionary<string, Dictionary<string, LocalizedString>>>();
foreach (var language in LocalizationOptions.Languages)
{
var resourceTexts = new Dictionary<string, Dictionary<string, LocalizedString>>();
foreach (var resource in localizationResources)
{
using (CultureHelper.Use(language.CultureName, language.UiCultureName))
{
var stringLocalizer = StringLocalizerFactory.Create(resource.ResourceType);
var localizedStrings = await stringLocalizer.GetAllStringsAsync(false, false, false);
var textHashKey = GetApplicationTextHashKey(resource.ResourceName, language.CultureName);
var textHashCache = await Cache.GetStringAsync(textHashKey, CancellationTokenProvider.Token);
var textHash = CalculateTextsHash(localizedStrings);
if (textHashCache == textHash)
{
continue;
}
var savedLocalizedStrings = await TextRepository.GetListAsync(
resource.ResourceName,
language.CultureName);
foreach (var localizedString in localizedStrings)
{
var findLocalizedString = savedLocalizedStrings.FirstOrDefault(x => x.Key == localizedString.Name);
if (findLocalizedString == null)
{
createTexts.Add(
new Text(
resource.ResourceName,
language.CultureName,
localizedString.Name,
localizedString.Value));
continue;
}
if (!string.Equals(findLocalizedString.Value, localizedString.Value, StringComparison.InvariantCultureIgnoreCase))
{
findLocalizedString.SetValue(localizedString.Value);
updateTexts.Add(findLocalizedString);
}
}
await Cache.SetStringAsync(textHashKey, textHash, CancellationTokenProvider.Token);
}
}
languageResourceTexts[language.CultureName] = resourceTexts;
}
if (createTexts.Any())
{
Logger.LogInformation("Saved {0} new texts.", createTexts.Count);
await TextRepository.InsertManyAsync(createTexts, cancellationToken: CancellationTokenProvider.Token);
}
if (updateTexts.Any())
{
Logger.LogInformation("Update {0} changed texts.", updateTexts.Count);
await TextRepository.UpdateManyAsync(updateTexts, cancellationToken: CancellationTokenProvider.Token);
}
}
private string GetApplicationDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_AbpLocalizationsUpdateLock";
}
private string GetApplicationResourceHashKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_AbpLocalizationResourcesHash";
}
private string GetApplicationLanguageHashKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_AbpLocalizationLanguagesHash";
}
private string GetApplicationTextHashKey(string resourceName, string cultureName)
{
return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_{resourceName}_{cultureName}_AbpLocalizationTextsHash";
}
private static string CalculateResourceHash(List<LocalizationResourceBase> localizationResources)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("LocalizationResources:");
stringBuilder.AppendLine(JsonSerializer.Serialize(localizationResources));
return stringBuilder
.ToString()
.ToMd5();
}
private static string CalculateLanguagesHash(List<LanguageInfo> languageInfos)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("LocalizationLanguages:");
stringBuilder.AppendLine(JsonSerializer.Serialize(languageInfos));
return stringBuilder
.ToString()
.ToMd5();
}
private static string CalculateTextsHash(IEnumerable<LocalizedString> localizers)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("LocalizationTexts:");
stringBuilder.AppendLine(JsonSerializer.Serialize(
localizers.Select(x => new NameValue(x.Name, x.Value)).ToList()));
return stringBuilder
.ToString()
.ToMd5();
}
}

26
aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs

@ -62,29 +62,19 @@ public class EfCoreTextRepository : EfCoreRepository<LocalizationDbContext, Text
.CountAsync(GetCancellationToken(cancellationToken));
}
public virtual List<Text> GetList(string resourceName = null, string cultureName = null)
{
return DbSet
.WhereIf(!resourceName.IsNullOrWhiteSpace(), x => x.ResourceName.Equals(resourceName))
.WhereIf(!cultureName.IsNullOrWhiteSpace(), x => x.CultureName.Equals(cultureName))
.ToList();
}
public async virtual Task<List<Text>> GetListAsync(
string resourceName = null,
string cultureName = null,
CancellationToken cancellationToken = default)
{
//var languages = (await GetDbContextAsync()).Set<Language>();
//var resources = (IQueryable<Resource>)(await GetDbContextAsync()).Set<Resource>();
//if (!resourceName.IsNullOrWhiteSpace())
//{
// resources = resources.Where(x => x.Name.Equals(resourceName));
//}
//var texts = await GetDbSetAsync();
//return await (from txts in texts
// join r in resources
// on txts.ResourceName equals r.Name
// join lg in languages
// on txts.CultureName equals lg.CultureName
// where r.Enable && lg.Enable
// select txts)
// .ToListAsync(GetCancellationToken(cancellationToken));
return await (await GetDbSetAsync())
.WhereIf(!resourceName.IsNullOrWhiteSpace(), x => x.ResourceName.Equals(resourceName))
.WhereIf(!cultureName.IsNullOrWhiteSpace(), x => x.CultureName.Equals(cultureName))

68
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItem.cs

@ -1,35 +1,35 @@
using System;
using System;
using Volo.Abp;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Saas.Tenants;
[Serializable]
[IgnoreMultiTenancy]
public class TenantCacheItem
{
private const string CacheKeyFormat = "i:{0},n:{1}";
public TenantConfiguration Value { get; set; }
public TenantCacheItem()
{
}
public TenantCacheItem(TenantConfiguration value)
{
Value = value;
}
public static string CalculateCacheKey(Guid? id, string name)
{
if (id == null && name.IsNullOrWhiteSpace())
{
throw new AbpException("Both id and name can't be invalid.");
}
return string.Format(CacheKeyFormat,
id?.ToString() ?? "null",
(name.IsNullOrWhiteSpace() ? "null" : name));
}
}
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Saas.Tenants;
[Serializable]
[IgnoreMultiTenancy]
public class TenantCacheItem
{
private const string CacheKeyFormat = "i:{0},n:{1}";
public TenantConfiguration Value { get; set; }
public TenantCacheItem()
{
}
public TenantCacheItem(TenantConfiguration value)
{
Value = value;
}
public static string CalculateCacheKey(Guid? id, string name)
{
if (id == null && name.IsNullOrWhiteSpace())
{
throw new AbpException("Both id and name can't be invalid.");
}
return string.Format(CacheKeyFormat,
id?.ToString() ?? "null",
(name.IsNullOrWhiteSpace() ? "null" : name));
}
}

16
aspnet-core/services/LY.MicroService.Applications.Single/DataSeeder/DataSeederWorker.cs

@ -0,0 +1,16 @@
namespace LY.MicroService.Applications.Single.DataSeeder;
public class DataSeederWorker : BackgroundService
{
protected IDataSeeder DataSeeder { get; }
public DataSeederWorker(IDataSeeder dataSeeder)
{
DataSeeder = dataSeeder;
}
protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
await DataSeeder.SeedAsync();
}
}

103
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/Components/ProfileManagementGroup/PersonalInfo/Default.cshtml

@ -1,103 +0,0 @@
@using Volo.Abp.Account.Localization
@using Volo.Abp.Users
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Localization
@using Volo.Abp.Account.Web.Pages.Account.Components.ProfileManagementGroup.PersonalInfo
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Volo.Abp.Data
@using Volo.Abp.Identity.Settings
@using Volo.Abp.Localization
@using Volo.Abp.Settings
@using Volo.Abp.ObjectExtending
@inject IHtmlLocalizer<AccountResource> L
@inject ICurrentUser CurrentUser
@inject ISettingProvider SettingManager
@inject IThemeManager ThemeManager
@inject IStringLocalizerFactory StringLocalizerFactory
@model Volo.Abp.Account.Web.Pages.Account.Components.ProfileManagementGroup.PersonalInfo.AccountProfilePersonalInfoManagementGroupViewComponent.PersonalInfoModel
@{
var isUserNameUpdateEnabled = string.Equals(await SettingManager.GetOrNullAsync(IdentitySettingNames.User.IsUserNameUpdateEnabled), "true",
StringComparison.OrdinalIgnoreCase);
var isEmailUpdateEnabled = string.Equals(await SettingManager.GetOrNullAsync(IdentitySettingNames.User.IsEmailUpdateEnabled), "true",
StringComparison.OrdinalIgnoreCase);
}
<h4>@L["PersonalSettings"]</h4><hr/>
<form method="post" id="PersonalSettingsForm">
<input asp-for="ConcurrencyStamp" />
<abp-input asp-for="UserName" readonly="!isUserNameUpdateEnabled"/>
<abp-row>
<abp-column size-md="_6">
<abp-input asp-for="Name"/>
</abp-column>
<abp-column size-md="_6">
<abp-input asp-for="Surname"/>
</abp-column>
</abp-row>
<abp-row>
<abp-column size-md="_9">
<abp-input asp-for="Email" readonly="!isEmailUpdateEnabled"/>
</abp-column>
<abp-column size-md="_3">
@if (CurrentUser.EmailVerified)
{
<abp-button button-type="Success" text="@L["Confirmed"].Value"/>
}
else
{
@*<abp-button href="/Account/SendEmailConfirm" button-type="Link" text="@L["Validation"].Value" />*@
<a href="/Account/SendEmailConfirm">@L["Validation"].Value</a>
}
</abp-column>
</abp-row>
<abp-input asp-for="PhoneNumber"/>
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<AccountProfilePersonalInfoManagementGroupViewComponent.PersonalInfoModel>())
{
var isAllowed = propertyInfo.Configuration.GetOrDefault(IdentityModuleExtensionConsts.ConfigurationNames.AllowUserToEdit);
if (isAllowed == null || !isAllowed.Equals(true))
{
continue;
}
if (!propertyInfo.Name.EndsWith("_Text"))
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
if (propertyInfo.Type.IsEnum)
{
Model.ExtraProperties.ToEnum(propertyInfo.Name, propertyInfo.Type);
}
<abp-select asp-for="ExtraProperties[propertyInfo.Name]"
name="ExtraProperties.@propertyInfo.Name"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.GetProperty(propertyInfo.Name + "_Text")"
autocomplete-selected-item-value="@Model.GetProperty(propertyInfo.Name)"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName">
</abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="ExtraProperties[propertyInfo.Name]"
name="ExtraProperties.@propertyInfo.Name"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.GetProperty(propertyInfo.Name))" />
}
}
}
<abp-button type="submit" button-type="Primary" text="@L["Submit"].Value"/>
</form>

28
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/Components/ProfileManagementGroup/PersonalInfo/Default.js

@ -1,28 +0,0 @@
(function ($) {
$(function () {
var l = abp.localization.getResource("AbpAccount");
$('#PersonalSettingsForm').submit(function (e) {
e.preventDefault();
if (!$('#PersonalSettingsForm').valid()) {
return false;
}
var input = $('#PersonalSettingsForm').serializeFormToObject();
volo.abp.account.profile.update(input).then(function (result) {
abp.notify.success(l('PersonalSettingsSaved'));
updateConcurrencyStamp();
});
});
});
abp.event.on('passwordChanged', updateConcurrencyStamp);
function updateConcurrencyStamp(){
volo.abp.account.profile.get().then(function(profile){
$("#ConcurrencyStamp").val(profile.concurrencyStamp);
});
}
})(jQuery);

17
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/EmailConfirm.cshtml

@ -1,17 +0,0 @@
@page
@inject IHtmlLocalizer<AccountResource> L
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@model LY.MicroService.Applications.Single.Pages.Account.EmailConfirmModel
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["EmailConfirm"]</h4>
<form method="post">
<abp-input asp-for="UserId"/>
<abp-input asp-for="ConfirmToken"/>
<a abp-button="Secondary" asp-page="./Login">@L["Cancel"]</a>
<abp-button type="submit" button-type="Primary" text="@L["Submit"].Value"/>
</form>
</div>
</div>

74
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/EmailConfirm.cshtml.cs

@ -1,74 +0,0 @@
using LINGYUN.Abp.Account;
using Microsoft.AspNetCore.Mvc;
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.Account.Localization;
using Volo.Abp.Account.Web.Pages.Account;
using Volo.Abp.Identity;
using Volo.Abp.Validation;
namespace LY.MicroService.Applications.Single.Pages.Account
{
public class EmailConfirmModel : AccountPageModel
{
[Required]
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid UserId { get; set; }
[Required]
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ConfirmToken { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
public IMyProfileAppService MyProfileAppService { get; set; }
public EmailConfirmModel()
{
LocalizationResourceType = typeof(AccountResource);
}
public async virtual Task<IActionResult> OnPostAsync()
{
try
{
ValidateModel();
await MyProfileAppService.ConfirmEmailAsync(
new ConfirmEmailInput
{
ConfirmToken = ConfirmToken,
});
}
catch (AbpIdentityResultException e)
{
if (!string.IsNullOrWhiteSpace(e.Message))
{
Alerts.Warning(GetLocalizeExceptionMessage(e));
return Page();
}
throw;
}
catch (AbpValidationException)
{
return Page();
}
return RedirectToPage("./ConfirmEmailConfirmation", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash
});
}
}
}

13
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/EmailConfirmConfirmation.cshtml

@ -1,13 +0,0 @@
@page
@model LY.MicroService.Applications.Single.Pages.Account.EmailConfirmConfirmationModel
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@inject IHtmlLocalizer<AccountResource> L
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["EmailConfirm"]</h4>
<p>@L["YourEmailIsSuccessfullyConfirm"]</p>
<a abp-button="Primary" href="@Url.Content(Model.ReturnUrl)">@L["GoToTheApplication"]</a>
</div>
</div>

23
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/EmailConfirmConfirmation.cshtml.cs

@ -1,23 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Volo.Abp.Account.Web.Pages.Account;
namespace LY.MicroService.Applications.Single.Pages.Account;
[AllowAnonymous]
public class EmailConfirmConfirmationModel : AccountPageModel
{
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
public async virtual Task<IActionResult> OnGetAsync()
{
ReturnUrl = await GetRedirectUrlAsync(ReturnUrl, ReturnUrlHash);
return Page();
}
}

26
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/SendCode.cshtml

@ -1,26 +0,0 @@
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@model LY.MicroService.Applications.Single.Pages.Account.SendCodeModel
@inject IHtmlLocalizer<AccountResource> L
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["TwoFactor"]</h4>
<form method="post" class="mt-4">
<abp-input asp-for="RememberMe" />
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
<div class="form-group">
<abp-select asp-for="Input.SelectedProvider" label="@L["SelectedProvider"].Value" asp-items="@Model.Providers"></abp-select>
</div>
<div class="d-grid gap-2">
<abp-button type="submit" button-type="Primary" class="mt-2 mb-3">@L["SendVerifyCode"]</abp-button>
</div>
<a asp-page="./Login" asp-all-route-data="@(new Dictionary<string, string> {{"returnUrl", Model.ReturnUrl}, {"returnUrlHash", Model.ReturnUrlHash}})">
<i class="fa fa-long-arrow-left"></i> @L["Login"]
</a>
</form>
</div>
</div>

128
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/SendCode.cshtml.cs

@ -1,128 +0,0 @@
using LINGYUN.Abp.Account.Emailing;
using LINGYUN.Abp.Identity.Settings;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Account.Localization;
using Volo.Abp.Account.Web.Pages.Account;
using Volo.Abp.Sms;
namespace LY.MicroService.Applications.Single.Pages.Account
{
public class SendCodeModel : AccountPageModel
{
[BindProperty]
public SendCodeInputModel Input { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public bool RememberMe { get; set; }
public IEnumerable<SelectListItem> Providers { get; set; }
protected ISmsSender SmsSender { get; }
protected IAccountEmailVerifySender AccountEmailVerifySender { get; }
public SendCodeModel(
ISmsSender smsSender,
IAccountEmailVerifySender accountEmailVerifySender)
{
SmsSender = smsSender;
AccountEmailVerifySender = accountEmailVerifySender;
LocalizationResourceType = typeof(AccountResource);
}
public virtual async Task<IActionResult> OnGetAsync()
{
Input = new SendCodeInputModel();
var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
// 双因素信息验证失败,一般都是超时了或者用户信息变更
Alerts.Warning(L["TwoFactorAuthenticationInvaidUser"]);
return Page();
}
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(user);
Providers = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return Page();
}
public virtual async Task<IActionResult> OnPostAsync()
{
var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
Alerts.Warning(L["TwoFactorAuthenticationInvaidUser"]);
return Page();
}
if (Input.SelectedProvider == "Authenticator")
{
// 用户通过邮件/短信链接进入授权页面
return RedirectToPage("VerifyAuthenticatorCode", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash,
rememberMe = RememberMe
});
}
// 生成验证码
var code = await UserManager.GenerateTwoFactorTokenAsync(user, Input.SelectedProvider);
if (string.IsNullOrWhiteSpace(code))
{
Alerts.Warning(L["InvaidGenerateTwoFactorToken"]);
return Page();
}
if (Input.SelectedProvider == "Email")
{
await AccountEmailVerifySender
.SendMailLoginVerifyCodeAsync(
code,
user.UserName,
user.Email);
}
else if (Input.SelectedProvider == "Phone")
{
var phoneNumber = await UserManager.GetPhoneNumberAsync(user);
var templateCode = await SettingProvider.GetOrNullAsync(IdentitySettingNames.User.SmsUserSignin);
Check.NotNullOrWhiteSpace(templateCode, nameof(IdentitySettingNames.User.SmsUserSignin));
// TODO: 以后扩展短信模板发送
var smsMessage = new SmsMessage(phoneNumber, code);
smsMessage.Properties.Add("code", code);
smsMessage.Properties.Add("TemplateCode", templateCode);
await SmsSender.SendAsync(smsMessage);
}
return RedirectToPage("VerifyCode", new
{
provider = Input.SelectedProvider,
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash,
rememberMe = RememberMe
});
}
}
public class SendCodeInputModel
{
public string SelectedProvider { get; set; }
}
}

16
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/SendEmailConfirm.cshtml

@ -1,16 +0,0 @@
@page
@inject IHtmlLocalizer<AccountResource> L
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@model LY.MicroService.Applications.Single.Pages.Account.SendEmailConfirmModel
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["EmailConfirm"]</h4>
<form method="post">
<abp-input asp-for="Email" readonly="true" />
<a abp-button="Secondary" asp-page="./Login">@L["Cancel"]</a>
<abp-button type="submit" button-type="Primary" text="@L["ClickToValidation"].Value"/>
</form>
</div>
</div>

75
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/SendEmailConfirm.cshtml.cs

@ -1,75 +0,0 @@
using LINGYUN.Abp.Account;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.Account.Localization;
using Volo.Abp.Account.Web.Pages.Account;
using Volo.Abp.Identity;
using Volo.Abp.Validation;
namespace LY.MicroService.Applications.Single.Pages.Account
{
public class SendEmailConfirmModel : AccountPageModel
{
[BindProperty(SupportsGet = true)]
public string Email { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
public IMyProfileAppService MyProfileAppService { get; set; }
public SendEmailConfirmModel()
{
LocalizationResourceType = typeof(AccountResource);
}
public virtual Task<IActionResult> OnGetAsync()
{
Email = CurrentUser.Email;
return Task.FromResult<IActionResult>(Page());
}
public async virtual Task<IActionResult> OnPostAsync()
{
try
{
ValidateModel();
await MyProfileAppService.SendEmailConfirmLinkAsync(
new SendEmailConfirmCodeDto
{
Email = Email,
AppName = "MVC",
ReturnUrl = ReturnUrl,
ReturnUrlHash = ReturnUrlHash
});
}
catch (AbpIdentityResultException e)
{
if (!string.IsNullOrWhiteSpace(e.Message))
{
Alerts.Warning(GetLocalizeExceptionMessage(e));
return Page();
}
throw;
}
catch (AbpValidationException)
{
return Page();
}
return RedirectToPage("~/Account/Manage", new
{
returnUrl = ReturnUrl
});
}
}
}

65
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/TwoFactorSupportedLoginModel.cs

@ -1,65 +0,0 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Account.Web;
using Volo.Abp.Account.Web.Pages.Account;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.OpenIddict;
using IdentityOptions = Microsoft.AspNetCore.Identity.IdentityOptions;
namespace LY.MicroService.Applications.Single.Pages.Account
{
/// <summary>
/// 重写登录模型,实现双因素登录
/// </summary>
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(LoginModel), typeof(OpenIddictSupportedLoginModel))]
public class TwoFactorSupportedLoginModel : OpenIddictSupportedLoginModel
{
public TwoFactorSupportedLoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IOptions<IdentityOptions> identityOptions,
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache,
AbpOpenIddictRequestHelper openIddictRequestHelper)
: base(schemeProvider, accountOptions, identityOptions, identityDynamicClaimsPrincipalContributorCache, openIddictRequestHelper)
{
}
protected async override Task<List<ExternalProviderModel>> GetExternalProviders()
{
var providers = await base.GetExternalProviders();
foreach (var provider in providers)
{
var localizedDisplayName = L[provider.DisplayName];
if (localizedDisplayName.ResourceNotFound)
{
localizedDisplayName = L["AuthenticationScheme:" + provider.DisplayName];
}
if (!localizedDisplayName.ResourceNotFound)
{
provider.DisplayName = localizedDisplayName.Value;
}
}
return providers;
}
protected override Task<IActionResult> TwoFactorLoginResultAsync()
{
// 重定向双因素认证页面
return Task.FromResult<IActionResult>(RedirectToPage("SendCode", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash,
rememberMe = LoginInput.RememberMe
}));
}
}
}

4
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/UseRecoveryCode.cshtml

@ -1,4 +0,0 @@
@page
@model LY.MicroService.Applications.Single.Pages.Account.UseRecoveryCodeModel
@{
}

11
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/UseRecoveryCode.cshtml.cs

@ -1,11 +0,0 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace LY.MicroService.Applications.Single.Pages.Account
{
public class UseRecoveryCodeModel : PageModel
{
public void OnGet()
{
}
}
}

26
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/VerifyAuthenticatorCode.cshtml

@ -1,26 +0,0 @@
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@model LY.MicroService.Applications.Single.Pages.Account.VerifyAuthenticatorCodeModel
@inject IHtmlLocalizer<AccountResource> L
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<form method="post" class="mt-4">
<input asp-for="RememberMe" />
<input asp-for="ReturnUrlHash" />
<div class="form-group">
<label asp-for="Input.VerifyCode"></label>
<input asp-for="Input.VerifyCode" class="form-control" />
<span asp-validation-for="Input.VerifyCode" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="RememberBrowser"></label>
<abp-input asp-for="RememberBrowser" />
</div>
<abp-button type="submit" button-type="Primary" class="btn-block btn-lg mt-3">@L["VerifyAuthenticatorCode"]</abp-button>
<a asp-page="./Login" asp-all-route-data="@(new Dictionary<string, string> {{"returnUrl", Model.ReturnUrl}, {"returnUrlHash", Model.ReturnUrlHash}})">
<i class="fa fa-long-arrow-left"></i> @L["Login"]
</a>
</form>
</div>
</div>

61
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/VerifyAuthenticatorCode.cshtml.cs

@ -1,61 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.Account.Web.Pages.Account;
namespace LY.MicroService.Applications.Single.Pages.Account
{
public class VerifyAuthenticatorCodeModel : AccountPageModel
{
[BindProperty]
public VerifyAuthenticatorCodeInputModel Input { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
[BindProperty(SupportsGet = true)]
public bool RememberBrowser { get; set; }
[HiddenInput]
public bool RememberMe { get; set; }
public virtual IActionResult OnGet()
{
Input = new VerifyAuthenticatorCodeInputModel();
return Page();
}
public virtual async Task<IActionResult> OnPostAsync()
{
var result = await SignInManager.TwoFactorAuthenticatorSignInAsync(Input.VerifyCode, RememberMe, RememberBrowser);
if (result.Succeeded)
{
return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
}
if (result.IsLockedOut)
{
Logger.LogWarning(7, "User account locked out.");
Alerts.Warning(L["UserLockedOutMessage"]);
return Page();
}
else
{
Alerts.Danger(L["TwoFactorAuthenticationInvaidUser"]);// TODO: ¸ü¶à״̬ÂëµÄ½â¶Á
return Page();
}
}
}
public class VerifyAuthenticatorCodeInputModel
{
[Required]
public string VerifyCode { get; set; }
}
}

29
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/VerifyCode.cshtml

@ -1,29 +0,0 @@
@page
@inject IHtmlLocalizer<AccountResource> L
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@model LY.MicroService.Applications.Single.Pages.Account.VerifyCodeModel
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<form method="post" class="mt-4">
<input asp-for="Provider" />
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
<input asp-for="RememberMe" />
<div class="form-group">
<abp-input asp-for="Input.VerifyCode" label="@L["VerifyCode"].Value" class="form-control" />
</div>
<abp-row>
<abp-column>
<abp-input asp-for="Input.RememberBrowser" label="@L["RememberBrowser"].Value" />
</abp-column>
</abp-row>
<div class="d-grid gap-2">
<abp-button type="submit" button-type="Primary" class="mt-2 mb-3">@L["VerifyAuthenticatorCode"]</abp-button>
</div>
<a asp-page="./SendCode" asp-all-route-data="@(new Dictionary<string, string> {{"returnUrl", Model.ReturnUrl}, {"returnUrlHash", Model.ReturnUrlHash}})">
<i class="fa fa-long-arrow-left"></i> @L["ReSendVerifyCode"]
</a>
</form>
</div>
</div>

92
aspnet-core/services/LY.MicroService.Applications.Single/Pages/Account/VerifyCode.cshtml.cs

@ -1,92 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.Account.Localization;
using Volo.Abp.Account.Web.Pages.Account;
namespace LY.MicroService.Applications.Single.Pages.Account
{
public class VerifyCodeModel : AccountPageModel
{
[BindProperty]
public VerifyCodeInputModel Input { get; set; }
/// <summary>
/// 双因素认证提供程序
/// </summary>
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string Provider { get; set; }
/// <summary>
/// 重定向Url
/// </summary>
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
/// <summary>
///
/// </summary>
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
/// <summary>
/// 是否记住登录状态
/// </summary>
[HiddenInput]
[BindProperty(SupportsGet = true)]
public bool RememberMe { get; set; }
public VerifyCodeModel()
{
LocalizationResourceType = typeof(AccountResource);
}
public virtual IActionResult OnGet()
{
Input = new VerifyCodeInputModel();
return Page();
}
public virtual async Task<IActionResult> OnPostAsync()
{
// 验证用户登录状态
var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
Alerts.Warning(L["TwoFactorAuthenticationInvaidUser"]);
return Page();
}
// 双因素登录
var result = await SignInManager.TwoFactorSignInAsync(Provider, Input.VerifyCode, RememberMe, Input.RememberBrowser);
if (result.Succeeded)
{
return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
}
if (result.IsLockedOut)
{
Logger.LogWarning(7, "User account locked out.");
Alerts.Warning(L["UserLockedOutMessage"]);
return Page();
}
else
{
Alerts.Danger(L["TwoFactorAuthenticationInvaidUser"]);// TODO: 更多状态码的解读
return Page();
}
}
}
public class VerifyCodeInputModel
{
/// <summary>
/// 是否在浏览器中记住登录状态
/// </summary>
public bool RememberBrowser { get; set; }
/// <summary>
/// 发送的验证码
/// </summary>
[Required]
public string VerifyCode { get; set; }
}
}

2
aspnet-core/services/LY.MicroService.Applications.Single/Properties/launchSettings.json

@ -14,7 +14,7 @@
"launchBrowser": false,
"applicationUrl": "http://0.0.0.0:30001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Production"
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"LY.MicroService.Applications.Single.Development": {

14
aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/AuthServerHttpApiHostModule.Configure.cs

@ -1,9 +1,9 @@
using DotNetCore.CAP;
using LINGYUN.Abp.ExceptionHandling;
using LINGYUN.Abp.ExceptionHandling.Emailing;
using LINGYUN.Abp.Gdpr.Localization;
using LINGYUN.Abp.Identity.Session;
using LINGYUN.Abp.Localization.CultureMap;
using LINGYUN.Abp.LocalizationManagement;
using LINGYUN.Abp.OpenIddict.Permissions;
using LINGYUN.Abp.Serilog.Enrichers.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
@ -40,12 +40,10 @@ using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Http.Client;
using Volo.Abp.Identity.Localization;
using Volo.Abp.Json;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
using Volo.Abp.OpenIddict.Localization;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Security.Claims;
using Volo.Abp.Threading;
@ -436,11 +434,6 @@ public partial class AuthServerHttpApiHostModule
{
options.Languages.Add(new LanguageInfo("en", "en", "English"));
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
options.UsePersistences(
typeof(GdprResource),
typeof(IdentityResource),
typeof(AbpOpenIddictResource));
});
Configure<AbpLocalizationCultureMapOptions>(options =>
@ -454,6 +447,11 @@ public partial class AuthServerHttpApiHostModule
options.CulturesMaps.Add(zhHansCultureMapInfo);
options.UiCulturesMaps.Add(zhHansCultureMapInfo);
});
Configure<AbpLocalizationManagementOptions>(options =>
{
options.SaveStaticLocalizationsToDatabase = true;
});
}
private void ConfigureCors(IServiceCollection services, IConfiguration configuration)

12
aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.Configure.cs

@ -1,7 +1,6 @@
using DotNetCore.CAP;
using LINGYUN.Abp.Account.Web;
using LINGYUN.Abp.Account.Web.OpenIddict;
using LINGYUN.Abp.Localization.CultureMap;
using LINGYUN.Abp.LocalizationManagement;
using LINGYUN.Abp.OpenIddict.AspNetCore.Session;
using LINGYUN.Abp.OpenIddict.LinkUser;
using LINGYUN.Abp.OpenIddict.Portal;
@ -25,7 +24,6 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.IdentityModel.Logging;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
@ -42,7 +40,6 @@ using Volo.Abp.Account.Localization;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Abp.Caching;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Json;
@ -370,8 +367,6 @@ public partial class AuthServerModule
options.Resources
.Get<AccountResource>()
.AddVirtualJson("/Localization/Resources");
options.UsePersistence<AccountResource>();
});
Configure<AbpLocalizationCultureMapOptions>(options =>
@ -385,6 +380,11 @@ public partial class AuthServerModule
options.CulturesMaps.Add(zhHansCultureMapInfo);
options.UiCulturesMaps.Add(zhHansCultureMapInfo);
});
Configure<AbpLocalizationManagementOptions>(options =>
{
options.SaveStaticLocalizationsToDatabase = true;
});
}
private void ConfigureTiming(IConfiguration configuration)
{

4
aspnet-core/services/LY.MicroService.AuthServer/LY.MicroService.AuthServer.csproj

@ -89,4 +89,8 @@
<ProjectReference Include="..\..\modules\platform\LINGYUN.Abp.Sms.Platform\LINGYUN.Abp.Sms.Platform.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\libs\jquery\" />
</ItemGroup>
</Project>

10
aspnet-core/services/LY.MicroService.AuthServer/gulpfile.js

@ -1,10 +0,0 @@
"use strict";
var gulp = require("gulp"),
path = require('path'),
copyResources = require('./node_modules/@abp/aspnetcore.mvc.ui/gulp/copy-resources.js');
exports.default = function(done){
copyResources(path.resolve('./'));
done();
};

3
aspnet-core/services/LY.MicroService.AuthServer/package.json

@ -3,6 +3,7 @@
"name": "my-app-authserver",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.leptonxlite": "4.0.4"
"@abp/aspnetcore.mvc.ui.theme.leptonxlite": "4.0.4",
"@abp/qrcode": "9.0.4"
}
}

10315
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/css/all.css

File diff suppressed because it is too large

1226
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/css/v4-shims.css

File diff suppressed because it is too large

BIN
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf

Binary file not shown.

BIN
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2

Binary file not shown.

BIN
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf

Binary file not shown.

BIN
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2

Binary file not shown.

BIN
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf

Binary file not shown.

BIN
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2

Binary file not shown.

79
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/abp/core/abp.js

@ -480,7 +480,11 @@ var abp = abp || {};
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < callbacks.length; i++) {
callbacks[i].apply(this, args);
try {
callbacks[i].apply(this, args);
} catch(e) {
console.error(e);
}
}
};
@ -748,52 +752,55 @@ var abp = abp || {};
return abp.clock.kind === 'Utc';
};
var toLocal = function (date) {
return new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
date.getMilliseconds()
);
};
// Normalize Date object or date string to standard string format that will be sent to server
abp.clock.normalizeToString = function (date) {
if (!date) {
return date;
}
var toUtc = function (date) {
return Date.UTC(
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
date.getUTCHours(),
date.getUTCMinutes(),
date.getUTCSeconds(),
date.getUTCMilliseconds()
);
};
var dateObj = date instanceof Date ? date : new Date(date);
if (isNaN(dateObj)) {
return date;
}
abp.clock.now = function () {
if (abp.clock.kind === 'Utc') {
return toUtc(new Date());
return dateObj.toISOString();
}
return new Date();
};
abp.clock.normalize = function (date) {
var kind = abp.clock.kind;
function padZero(num) {
return num < 10 ? '0' + num : num;
}
if (kind === 'Unspecified') {
return date;
function padMilliseconds(num) {
if (num < 10) return '00' + num;
if (num < 100) return '0' + num;
return num;
}
// yyyy-MM-ddTHH:mm:ss.SSS
return dateObj.getFullYear() + '-' +
padZero(dateObj.getMonth() + 1) + '-' +
padZero(dateObj.getDate()) + 'T' +
padZero(dateObj.getHours()) + ':' +
padZero(dateObj.getMinutes()) + ':' +
padZero(dateObj.getSeconds()) + '.' +
padMilliseconds(dateObj.getMilliseconds());
};
if (kind === 'Local') {
return toLocal(date);
// Normalize date string to locale date string that will be displayed to user
abp.clock.normalizeToLocaleString = function (dateString) {
if (!dateString) {
return dateString;
}
if (kind === 'Utc') {
return toUtc(date);
var date = new Date(dateString);
if (isNaN(date)) {
return dateString;
}
};
//TODO: Get timezone setting and pass it to toLocaleString
return date.toLocaleString();
}
/* FEATURES *************************************************/

99
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/bootstrap/css/bootstrap.rtl.min.css.map

File diff suppressed because one or more lines are too long

34
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/bootstrap/js/bootstrap.bundle.min.js

File diff suppressed because one or more lines are too long

396
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/datatables.net-bs5/css/dataTables.bootstrap5.css

@ -1,4 +1,3 @@
@charset "UTF-8";
:root {
--dt-row-selected: 13, 110, 253;
--dt-row-selected-text: 255, 255, 255;
@ -18,78 +17,133 @@ table.dataTable td.dt-control {
}
table.dataTable td.dt-control:before {
display: inline-block;
color: rgba(0, 0, 0, 0.5);
content: "▶";
box-sizing: border-box;
content: "";
border-top: 5px solid transparent;
border-left: 10px solid rgba(0, 0, 0, 0.5);
border-bottom: 5px solid transparent;
border-right: 0px solid transparent;
}
table.dataTable tr.dt-hasChild td.dt-control:before {
content: "▼";
border-top: 10px solid rgba(0, 0, 0, 0.5);
border-left: 5px solid transparent;
border-bottom: 0px solid transparent;
border-right: 5px solid transparent;
}
table.dataTable tfoot:empty {
display: none;
}
html.dark table.dataTable td.dt-control:before,
:root[data-bs-theme=dark] table.dataTable td.dt-control:before {
color: rgba(255, 255, 255, 0.5);
:root[data-bs-theme=dark] table.dataTable td.dt-control:before,
:root[data-theme=dark] table.dataTable td.dt-control:before {
border-left-color: rgba(255, 255, 255, 0.5);
}
html.dark table.dataTable tr.dt-hasChild td.dt-control:before,
:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before {
color: rgba(255, 255, 255, 0.5);
:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before,
:root[data-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before {
border-top-color: rgba(255, 255, 255, 0.5);
border-left-color: transparent;
}
table.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,
table.dataTable thead > tr > td.sorting,
table.dataTable thead > tr > td.sorting_asc,
table.dataTable thead > tr > td.sorting_desc,
table.dataTable thead > tr > td.sorting_asc_disabled,
table.dataTable thead > tr > td.sorting_desc_disabled {
cursor: pointer;
position: relative;
padding-right: 26px;
}
table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,
table.dataTable thead > tr > td.sorting:before,
table.dataTable thead > tr > td.sorting:after,
table.dataTable thead > tr > td.sorting_asc:before,
table.dataTable thead > tr > td.sorting_asc:after,
table.dataTable thead > tr > td.sorting_desc:before,
table.dataTable thead > tr > td.sorting_desc:after,
table.dataTable thead > tr > td.sorting_asc_disabled:before,
table.dataTable thead > tr > td.sorting_asc_disabled:after,
table.dataTable thead > tr > td.sorting_desc_disabled:before,
table.dataTable thead > tr > td.sorting_desc_disabled:after {
div.dt-scroll {
width: 100%;
}
div.dt-scroll-body thead tr,
div.dt-scroll-body tfoot tr {
height: 0;
}
div.dt-scroll-body thead tr th, div.dt-scroll-body thead tr td,
div.dt-scroll-body tfoot tr th,
div.dt-scroll-body tfoot tr td {
height: 0 !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
border-top-width: 0px !important;
border-bottom-width: 0px !important;
}
div.dt-scroll-body thead tr th div.dt-scroll-sizing, div.dt-scroll-body thead tr td div.dt-scroll-sizing,
div.dt-scroll-body tfoot tr th div.dt-scroll-sizing,
div.dt-scroll-body tfoot tr td div.dt-scroll-sizing {
height: 0 !important;
overflow: hidden !important;
}
table.dataTable thead > tr > th:active,
table.dataTable thead > tr > td:active {
outline: none;
}
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before {
position: absolute;
display: block;
bottom: 50%;
content: "\25B2";
content: "\25B2"/"";
}
table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
position: absolute;
display: block;
top: 50%;
content: "\25BC";
content: "\25BC"/"";
}
table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc, table.dataTable thead > tr > th.dt-ordering-asc, table.dataTable thead > tr > th.dt-ordering-desc,
table.dataTable thead > tr > td.dt-orderable-asc,
table.dataTable thead > tr > td.dt-orderable-desc,
table.dataTable thead > tr > td.dt-ordering-asc,
table.dataTable thead > tr > td.dt-ordering-desc {
position: relative;
padding-right: 30px;
}
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order,
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order,
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order {
position: absolute;
right: 12px;
top: 0;
bottom: 0;
width: 12px;
}
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
left: 0;
opacity: 0.125;
right: 10px;
line-height: 9px;
font-size: 0.8em;
}
table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,
table.dataTable thead > tr > td.sorting:before,
table.dataTable thead > tr > td.sorting_asc:before,
table.dataTable thead > tr > td.sorting_desc:before,
table.dataTable thead > tr > td.sorting_asc_disabled:before,
table.dataTable thead > tr > td.sorting_desc_disabled:before {
bottom: 50%;
content: "▲";
content: "▲"/"";
}
table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,
table.dataTable thead > tr > td.sorting:after,
table.dataTable thead > tr > td.sorting_asc:after,
table.dataTable thead > tr > td.sorting_desc:after,
table.dataTable thead > tr > td.sorting_asc_disabled:after,
table.dataTable thead > tr > td.sorting_desc_disabled:after {
top: 50%;
content: "▼";
content: "▼"/"";
table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc,
table.dataTable thead > tr > td.dt-orderable-asc,
table.dataTable thead > tr > td.dt-orderable-desc {
cursor: pointer;
}
table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,
table.dataTable thead > tr > td.sorting_asc:before,
table.dataTable thead > tr > td.sorting_desc:after {
table.dataTable thead > tr > th.dt-orderable-asc:hover, table.dataTable thead > tr > th.dt-orderable-desc:hover,
table.dataTable thead > tr > td.dt-orderable-asc:hover,
table.dataTable thead > tr > td.dt-orderable-desc:hover {
outline: 2px solid rgba(0, 0, 0, 0.05);
outline-offset: -2px;
}
table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
opacity: 0.6;
}
table.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,
table.dataTable thead > tr > td.sorting_desc_disabled:after,
table.dataTable thead > tr > td.sorting_asc_disabled:before {
table.dataTable thead > tr > th.sorting_desc_disabled span.dt-column-order:after, table.dataTable thead > tr > th.sorting_asc_disabled span.dt-column-order:before,
table.dataTable thead > tr > td.sorting_desc_disabled span.dt-column-order:after,
table.dataTable thead > tr > td.sorting_asc_disabled span.dt-column-order:before {
display: none;
}
table.dataTable thead > tr > th:active,
@ -97,30 +151,39 @@ table.dataTable thead > tr > td:active {
outline: none;
}
div.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,
div.dataTables_scrollBody > table.dataTable > thead > tr > td:before,
div.dataTables_scrollBody > table.dataTable > thead > tr > td:after {
display: none;
div.dt-scroll-body > table.dataTable > thead > tr > th,
div.dt-scroll-body > table.dataTable > thead > tr > td {
overflow: hidden;
}
div.dataTables_processing {
:root.dark table.dataTable thead > tr > th.dt-orderable-asc:hover, :root.dark table.dataTable thead > tr > th.dt-orderable-desc:hover,
:root.dark table.dataTable thead > tr > td.dt-orderable-asc:hover,
:root.dark table.dataTable thead > tr > td.dt-orderable-desc:hover,
:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-asc:hover,
:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-desc:hover,
:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-asc:hover,
:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-desc:hover {
outline: 2px solid rgba(255, 255, 255, 0.05);
}
div.dt-processing {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
margin-left: -100px;
margin-top: -26px;
margin-top: -22px;
text-align: center;
padding: 2px;
z-index: 10;
}
div.dataTables_processing > div:last-child {
div.dt-processing > div:last-child {
position: relative;
width: 80px;
height: 15px;
margin: 1em auto;
}
div.dataTables_processing > div:last-child > div {
div.dt-processing > div:last-child > div {
position: absolute;
top: 0;
width: 13px;
@ -130,19 +193,19 @@ div.dataTables_processing > div:last-child > div {
background: rgb(var(--dt-row-selected));
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
div.dataTables_processing > div:last-child > div:nth-child(1) {
div.dt-processing > div:last-child > div:nth-child(1) {
left: 8px;
animation: datatables-loader-1 0.6s infinite;
}
div.dataTables_processing > div:last-child > div:nth-child(2) {
div.dt-processing > div:last-child > div:nth-child(2) {
left: 8px;
animation: datatables-loader-2 0.6s infinite;
}
div.dataTables_processing > div:last-child > div:nth-child(3) {
div.dt-processing > div:last-child > div:nth-child(3) {
left: 32px;
animation: datatables-loader-2 0.6s infinite;
}
div.dataTables_processing > div:last-child > div:nth-child(4) {
div.dt-processing > div:last-child > div:nth-child(4) {
left: 56px;
animation: datatables-loader-3 0.6s infinite;
}
@ -174,13 +237,21 @@ div.dataTables_processing > div:last-child > div:nth-child(4) {
table.dataTable.nowrap th, table.dataTable.nowrap td {
white-space: nowrap;
}
table.dataTable th,
table.dataTable td {
box-sizing: border-box;
}
table.dataTable th.dt-type-numeric, table.dataTable th.dt-type-date,
table.dataTable td.dt-type-numeric,
table.dataTable td.dt-type-date {
text-align: right;
}
table.dataTable th.dt-left,
table.dataTable td.dt-left {
text-align: left;
}
table.dataTable th.dt-center,
table.dataTable td.dt-center,
table.dataTable td.dataTables_empty {
table.dataTable td.dt-center {
text-align: center;
}
table.dataTable th.dt-right,
@ -195,6 +266,11 @@ table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap {
white-space: nowrap;
}
table.dataTable th.dt-empty,
table.dataTable td.dt-empty {
text-align: center;
vertical-align: top;
}
table.dataTable thead th,
table.dataTable thead td,
table.dataTable tfoot th,
@ -257,182 +333,180 @@ table.dataTable tbody td.dt-body-nowrap {
* ©2020 SpryMedia Ltd, all rights reserved.
* License: MIT datatables.net/license/mit
*/
table.dataTable {
table.table.dataTable {
clear: both;
margin-top: 6px !important;
margin-bottom: 6px !important;
max-width: none !important;
border-collapse: separate !important;
margin-bottom: 0;
max-width: none;
border-spacing: 0;
}
table.dataTable td,
table.dataTable th {
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
table.dataTable td.dataTables_empty,
table.dataTable th.dataTables_empty {
text-align: center;
}
table.dataTable.nowrap th,
table.dataTable.nowrap td {
white-space: nowrap;
}
table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
box-shadow: none;
}
table.dataTable > tbody > tr {
table.table.dataTable > :not(caption) > * > * {
background-color: var(--bs-table-bg);
}
table.table.dataTable > tbody > tr {
background-color: transparent;
}
table.dataTable > tbody > tr.selected > * {
table.table.dataTable > tbody > tr.selected > * {
box-shadow: inset 0 0 0 9999px rgb(13, 110, 253);
box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));
color: rgb(255, 255, 255);
color: rgb(var(--dt-row-selected-text));
}
table.dataTable > tbody > tr.selected a {
table.table.dataTable > tbody > tr.selected a {
color: rgb(9, 10, 11);
color: rgb(var(--dt-row-selected-link));
}
table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);
}
table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1).selected > * {
table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1).selected > * {
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);
}
table.dataTable.table-hover > tbody > tr:hover > * {
table.table.dataTable.table-hover > tbody > tr:hover > * {
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);
}
table.dataTable.table-hover > tbody > tr.selected:hover > * {
table.table.dataTable.table-hover > tbody > tr.selected:hover > * {
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);
}
div.dataTables_wrapper div.dataTables_length label {
div.dt-container div.dt-layout-start > *:not(:last-child) {
margin-right: 1em;
}
div.dt-container div.dt-layout-end > *:not(:first-child) {
margin-left: 1em;
}
div.dt-container div.dt-layout-full {
width: 100%;
}
div.dt-container div.dt-layout-full > *:only-child {
margin-left: auto;
margin-right: auto;
}
div.dt-container div.dt-layout-table > div {
display: block !important;
}
@media screen and (max-width: 767px) {
div.dt-container div.dt-layout-start > *:not(:last-child) {
margin-right: 0;
}
div.dt-container div.dt-layout-end > *:not(:first-child) {
margin-left: 0;
}
}
div.dt-container div.dt-length label {
font-weight: normal;
text-align: left;
white-space: nowrap;
}
div.dataTables_wrapper div.dataTables_length select {
div.dt-container div.dt-length select {
width: auto;
display: inline-block;
margin-right: 0.5em;
}
div.dataTables_wrapper div.dataTables_filter {
div.dt-container div.dt-search {
text-align: right;
}
div.dataTables_wrapper div.dataTables_filter label {
div.dt-container div.dt-search label {
font-weight: normal;
white-space: nowrap;
text-align: left;
}
div.dataTables_wrapper div.dataTables_filter input {
div.dt-container div.dt-search input {
margin-left: 0.5em;
display: inline-block;
width: auto;
}
div.dataTables_wrapper div.dataTables_info {
padding-top: 0.85em;
}
div.dataTables_wrapper div.dataTables_paginate {
div.dt-container div.dt-paging {
margin: 0;
white-space: nowrap;
text-align: right;
}
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
div.dt-container div.dt-paging ul.pagination {
margin: 2px 0;
white-space: nowrap;
justify-content: flex-end;
flex-wrap: wrap;
}
div.dataTables_wrapper div.dt-row {
div.dt-container div.dt-row {
position: relative;
}
div.dataTables_scrollHead table.dataTable {
div.dt-scroll-head table.dataTable {
margin-bottom: 0 !important;
}
div.dataTables_scrollBody > table {
div.dt-scroll-body {
border-bottom-color: var(--bs-border-color);
border-bottom-width: var(--bs-border-width);
border-bottom-style: solid;
}
div.dt-scroll-body > table {
border-top: none;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
div.dataTables_scrollBody > table > thead .sorting:before,
div.dataTables_scrollBody > table > thead .sorting_asc:before,
div.dataTables_scrollBody > table > thead .sorting_desc:before,
div.dataTables_scrollBody > table > thead .sorting:after,
div.dataTables_scrollBody > table > thead .sorting_asc:after,
div.dataTables_scrollBody > table > thead .sorting_desc:after {
display: none;
div.dt-scroll-body > table > tbody > tr:first-child {
border-top-width: 0;
}
div.dataTables_scrollBody > table > tbody tr:first-child th,
div.dataTables_scrollBody > table > tbody tr:first-child td {
border-top: none;
div.dt-scroll-body > table > thead > tr {
border-width: 0 !important;
}
div.dt-scroll-body > table > tbody > tr:last-child > * {
border-bottom: none;
}
div.dataTables_scrollFoot > .dataTables_scrollFootInner {
div.dt-scroll-foot > .dt-scroll-footInner {
box-sizing: content-box;
}
div.dataTables_scrollFoot > .dataTables_scrollFootInner > table {
div.dt-scroll-foot > .dt-scroll-footInner > table {
margin-top: 0 !important;
border-top: none;
}
div.dt-scroll-foot > .dt-scroll-footInner > table > tfoot > tr:first-child {
border-top-width: 0 !important;
}
@media screen and (max-width: 767px) {
div.dataTables_wrapper div.dataTables_length,
div.dataTables_wrapper div.dataTables_filter,
div.dataTables_wrapper div.dataTables_info,
div.dataTables_wrapper div.dataTables_paginate {
div.dt-container div.dt-length,
div.dt-container div.dt-search,
div.dt-container div.dt-info,
div.dt-container div.dt-paging {
text-align: center;
}
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
div.dt-container .row {
--bs-gutter-y: 0.5rem;
}
div.dt-container div.dt-paging ul.pagination {
justify-content: center !important;
}
}
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {
table.dataTable.table-sm > thead > tr th.dt-orderable-asc, table.dataTable.table-sm > thead > tr th.dt-orderable-desc, table.dataTable.table-sm > thead > tr th.dt-ordering-asc, table.dataTable.table-sm > thead > tr th.dt-ordering-desc,
table.dataTable.table-sm > thead > tr td.dt-orderable-asc,
table.dataTable.table-sm > thead > tr td.dt-orderable-desc,
table.dataTable.table-sm > thead > tr td.dt-ordering-asc,
table.dataTable.table-sm > thead > tr td.dt-ordering-desc {
padding-right: 20px;
}
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled):before, table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled):after {
table.dataTable.table-sm > thead > tr th.dt-orderable-asc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-orderable-desc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-ordering-asc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-ordering-desc span.dt-column-order,
table.dataTable.table-sm > thead > tr td.dt-orderable-asc span.dt-column-order,
table.dataTable.table-sm > thead > tr td.dt-orderable-desc span.dt-column-order,
table.dataTable.table-sm > thead > tr td.dt-ordering-asc span.dt-column-order,
table.dataTable.table-sm > thead > tr td.dt-ordering-desc span.dt-column-order {
right: 5px;
}
table.table-bordered.dataTable {
border-right-width: 0;
}
table.table-bordered.dataTable thead tr:first-child th,
table.table-bordered.dataTable thead tr:first-child td {
border-top-width: 1px;
}
table.table-bordered.dataTable th,
table.table-bordered.dataTable td {
border-left-width: 0;
}
table.table-bordered.dataTable th:first-child, table.table-bordered.dataTable th:first-child,
table.table-bordered.dataTable td:first-child,
table.table-bordered.dataTable td:first-child {
border-left-width: 1px;
}
table.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,
table.table-bordered.dataTable td:last-child,
table.table-bordered.dataTable td:last-child {
border-right-width: 1px;
}
table.table-bordered.dataTable th,
table.table-bordered.dataTable td {
border-bottom-width: 1px;
}
div.dataTables_scrollHead table.table-bordered {
div.dt-scroll-head table.table-bordered {
border-bottom-width: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row {
div.table-responsive > div.dt-container > div.row {
margin: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child {
div.table-responsive > div.dt-container > div.row > div[class^=col-]:first-child {
padding-left: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child {
div.table-responsive > div.dt-container > div.row > div[class^=col-]:last-child {
padding-right: 0;
}

178
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/datatables.net-bs5/js/dataTables.bootstrap5.js

@ -1,5 +1,5 @@
/*! DataTables Bootstrap 5 integration
* 2020 SpryMedia Ltd - datatables.net/license
* © SpryMedia Ltd - datatables.net/license
*/
(function( factory ){
@ -43,15 +43,14 @@
// Browser
factory( jQuery, window, document );
}
}(function( $, window, document, undefined ) {
}(function( $, window, document ) {
'use strict';
var DataTable = $.fn.dataTable;
/**
* DataTables integration for Bootstrap 5. This requires Bootstrap 5 and
* DataTables 1.10 or newer.
* DataTables integration for Bootstrap 5.
*
* This file sets the defaults and adds options to DataTables to style its
* controls using Bootstrap. See https://datatables.net/manual/styling/bootstrap
@ -60,151 +59,62 @@ var DataTable = $.fn.dataTable;
/* Set the defaults for DataTables initialisation */
$.extend( true, DataTable.defaults, {
dom:
"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
"<'row dt-row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
renderer: 'bootstrap'
} );
/* Default class modification */
$.extend( DataTable.ext.classes, {
sWrapper: "dataTables_wrapper dt-bootstrap5",
sFilterInput: "form-control form-control-sm",
sLengthSelect: "form-select form-select-sm",
sProcessing: "dataTables_processing card",
sPageButton: "paginate_button page-item"
$.extend( true, DataTable.ext.classes, {
container: "dt-container dt-bootstrap5",
search: {
input: "form-control form-control-sm"
},
length: {
select: "form-select form-select-sm"
},
processing: {
container: "dt-processing card"
},
layout: {
row: 'row mt-2 justify-content-between',
cell: 'd-md-flex justify-content-between align-items-center',
tableCell: 'col-12',
start: 'dt-layout-start col-md-auto me-auto',
end: 'dt-layout-end col-md-auto ms-auto',
full: 'dt-layout-full col-md'
}
} );
/* Bootstrap paging button renderer */
DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {
var api = new DataTable.Api( settings );
var classes = settings.oClasses;
var lang = settings.oLanguage.oPaginate;
var aria = settings.oLanguage.oAria.paginate || {};
var btnDisplay, btnClass;
var attach = function( container, buttons ) {
var i, ien, node, button;
var clickHandler = function ( e ) {
e.preventDefault();
if ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {
api.page( e.data.action ).draw( 'page' );
}
};
for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
button = buttons[i];
if ( Array.isArray( button ) ) {
attach( container, button );
}
else {
btnDisplay = '';
btnClass = '';
switch ( button ) {
case 'ellipsis':
btnDisplay = '&#x2026;';
btnClass = 'disabled';
break;
case 'first':
btnDisplay = lang.sFirst;
btnClass = button + (page > 0 ?
'' : ' disabled');
break;
case 'previous':
btnDisplay = lang.sPrevious;
btnClass = button + (page > 0 ?
'' : ' disabled');
break;
case 'next':
btnDisplay = lang.sNext;
btnClass = button + (page < pages-1 ?
'' : ' disabled');
break;
case 'last':
btnDisplay = lang.sLast;
btnClass = button + (page < pages-1 ?
'' : ' disabled');
break;
default:
btnDisplay = button + 1;
btnClass = page === button ?
'active' : '';
break;
}
if ( btnDisplay ) {
var disabled = btnClass.indexOf('disabled') !== -1;
node = $('<li>', {
'class': classes.sPageButton+' '+btnClass,
'id': idx === 0 && typeof button === 'string' ?
settings.sTableId +'_'+ button :
null
} )
.append( $('<a>', {
'href': disabled ? null : '#',
'aria-controls': settings.sTableId,
'aria-disabled': disabled ? 'true' : null,
'aria-label': aria[ button ],
'role': 'link',
'aria-current': btnClass === 'active' ? 'page' : null,
'data-dt-idx': button,
'tabindex': disabled ? -1 : settings.iTabIndex,
'class': 'page-link'
} )
.html( btnDisplay )
)
.appendTo( container );
settings.oApi._fnBindAction(
node, {action: button}, clickHandler
);
}
}
}
};
DataTable.ext.renderer.pagingButton.bootstrap = function (settings, buttonType, content, active, disabled) {
var btnClasses = ['dt-paging-button', 'page-item'];
var hostEl = $(host);
// IE9 throws an 'unknown error' if document.activeElement is used
// inside an iframe or frame.
var activeEl;
try {
// Because this approach is destroying and recreating the paging
// elements, focus is lost on the select button which is bad for
// accessibility. So we want to restore focus once the draw has
// completed
activeEl = hostEl.find(document.activeElement).data('dt-idx');
if (active) {
btnClasses.push('active');
}
catch (e) {}
var paginationEl = hostEl.children('ul.pagination');
if (paginationEl.length) {
paginationEl.empty();
}
else {
paginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination');
if (disabled) {
btnClasses.push('disabled')
}
attach(
paginationEl,
buttons
);
var li = $('<li>').addClass(btnClasses.join(' '));
var a = $('<button>', {
'class': 'page-link',
role: 'link',
type: 'button'
})
.html(content)
.appendTo(li);
return {
display: li,
clicker: a
};
};
if ( activeEl !== undefined ) {
hostEl.find('[data-dt-idx='+activeEl+']').trigger('focus');
}
DataTable.ext.renderer.pagingContainer.bootstrap = function (settings, buttonEls) {
return $('<ul/>').addClass('pagination').append(buttonEls);
};

15733
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/datatables.net/js/jquery.dataTables.js

File diff suppressed because it is too large

11
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js

@ -1,7 +1,10 @@
// Unobtrusive validation support library for jQuery and jQuery Validate
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// @version v3.2.12
/**
* @license
* Unobtrusive validation support library for jQuery and jQuery Validate
* Copyright (c) .NET Foundation. All rights reserved.
* Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
* @version v4.0.0
*/
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
/*global document: false, jQuery: false */

29
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation/jquery.validate.js

@ -1,9 +1,9 @@
/*!
* jQuery Validation Plugin v1.20.0
* jQuery Validation Plugin v1.21.0
*
* https://jqueryvalidation.org/
*
* Copyright (c) 2023 Jörn Zaefferer
* Copyright (c) 2024 Jörn Zaefferer
* Released under the MIT license
*/
(function( factory ) {
@ -293,6 +293,7 @@ $.extend( $.validator, {
onsubmit: true,
ignore: ":hidden",
ignoreTitle: false,
customElements: [],
onfocusin: function( element ) {
this.lastActive = element;
@ -440,17 +441,17 @@ $.extend( $.validator, {
settings[ eventType ].call( validator, this, event );
}
}
var focusListeners = [ ":text", "[type='password']", "[type='file']", "select", "textarea", "[type='number']", "[type='search']",
"[type='tel']", "[type='url']", "[type='email']", "[type='datetime']", "[type='date']", "[type='month']",
"[type='week']", "[type='time']", "[type='datetime-local']", "[type='range']", "[type='color']",
"[type='radio']", "[type='checkbox']", "[contenteditable]", "[type='button']" ];
var clickListeners = [ "select", "option", "[type='radio']", "[type='checkbox']" ];
$( this.currentForm )
.on( "focusin.validate focusout.validate keyup.validate",
":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +
"[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +
"[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +
"[type='radio'], [type='checkbox'], [contenteditable], [type='button']", delegate )
.on( "focusin.validate focusout.validate keyup.validate", focusListeners.concat( this.settings.customElements ).join( ", " ), delegate )
// Support: Chrome, oldIE
// "select" is provided as event.target when clicking a option
.on( "click.validate", "select, option, [type='radio'], [type='checkbox']", delegate );
.on( "click.validate", clickListeners.concat( this.settings.customElements ).join( ", " ), delegate );
if ( this.settings.invalidHandler ) {
$( this.currentForm ).on( "invalid-form.validate", this.settings.invalidHandler );
@ -647,11 +648,12 @@ $.extend( $.validator, {
elements: function() {
var validator = this,
rulesCache = {};
rulesCache = {},
selectors = [ "input", "select", "textarea", "[contenteditable]" ];
// Select all valid inputs inside the form (no submit or reset buttons)
return $( this.currentForm )
.find( "input, select, textarea, [contenteditable]" )
.find( selectors.concat( this.settings.customElements ).join( ", " ) )
.not( ":submit, :reset, :image, :disabled" )
.not( this.settings.ignore )
.filter( function() {
@ -1501,7 +1503,7 @@ $.extend( $.validator, {
// https://jqueryvalidation.org/number-method/
number: function( value, element ) {
return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value );
return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:-?\.\d+)?$/.test( value );
},
// https://jqueryvalidation.org/digits-method/
@ -1612,11 +1614,12 @@ $.extend( $.validator, {
param = typeof param === "string" && { url: param } || param;
optionDataString = $.param( $.extend( { data: value }, param.data ) );
if ( previous.old === optionDataString ) {
if ( previous.valid !== null && previous.old === optionDataString ) {
return previous.valid;
}
previous.old = optionDataString;
previous.valid = null;
validator = this;
this.startRequest( element );
data = {};

31
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation/localization/messages_ar.js

@ -29,7 +29,36 @@ $.extend( $.validator.messages, {
rangelength: $.validator.format( "عدد الحروف يجب أن يكون بين {0} و {1}" ),
range: $.validator.format( "رجاء إدخال عدد قيمته بين {0} و {1}" ),
max: $.validator.format( "رجاء إدخال عدد أقل من أو يساوي {0}" ),
min: $.validator.format( "رجاء إدخال عدد أكبر من أو يساوي {0}" )
min: $.validator.format( "رجاء إدخال عدد أكبر من أو يساوي {0}" ),
step: $.validator.format( "يرجى تقديم قيمة من مضاعفات {0}" ),
maxWords: $.validator.format( "يرجى تقديم ما لا يزيد عن {0} كلمات" ),
minWords: $.validator.format( "يرجى تقديم {0} كلمات على الأقل" ),
rangeWords: $.validator.format( "يرجى تقديم ما بين {0} و{1} كلمة" ),
letterswithbasicpunc: "يرجى تقديم الحروف وعلامات الترقيم فقط",
alphanumeric: "يرجى تقديم الحروف والأرقام والمسافات والتسطير فقط",
lettersonly: "يرجى تقديم الحروف فقط",
nowhitespace: "من فضلك لا تدخل المساحات البيضاء",
ziprange: "يرجى تقديم الرمز البريدي بين 902xx-xxxx و905-xx-xxxx",
integer: "يرجى تقديم رقم غير عشري موجب أو سالب",
vinUS: "يرجى تقديم رقم تعريف المركبة (VIN)",
dateITA: "يرجى تقديم تاريخ صالح",
time: "يرجى تقديم وقت صالح بين 00:00 و23:59",
phoneUS: "الرجاء تقديم رقم هاتف صالح",
phoneUK: "الرجاء تقديم رقم هاتف صالح",
mobileUK: "يرجى تقديم رقم هاتف محمول صالح",
strippedminlength: $.validator.format( "يرجى تقديم {0} حرفًا على الأقل" ),
email2: "يرجى تقديم عنوان بريد إلكتروني صالح",
url2: "يرجى إدخال عنوان بريد إلكتروني صحيح",
creditcardtypes: "يرجى تقديم رقم بطاقة ائتمان صالح",
currency: "يرجى تقديم عملة صالحة",
ipv4: "يرجى تقديم عنوان IP v4 صالح",
ipv6: "يرجى تقديم عنوان IP v6 صالح",
require_from_group: $.validator.format( "يرجى تقديم ما لا يقل عن {0} من هذه الحقول" ),
nifES: "يرجى تقديم رقم TIN صالح",
nieES: "يرجى تقديم رقم NIE صالح",
cifES: "يرجى تقديم رقم CIF صالح",
postalCodeCA: "يرجى تقديم رمز بريدي صالح",
pattern: "التنسيق غير صالح"
} );
return $;
}));

6
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation/localization/messages_ar.min.js

@ -1,4 +1,4 @@
/*! jQuery Validation Plugin - v1.20.0 - 10/10/2023
/*! jQuery Validation Plugin - v1.21.0 - 7/17/2024
* https://jqueryvalidation.org/
* Copyright (c) 2023 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"هذا الحقل إلزامي",remote:"يرجى تصحيح هذا الحقل للمتابعة",email:"رجاء إدخال عنوان بريد إلكتروني صحيح",url:"رجاء إدخال عنوان موقع إلكتروني صحيح",date:"رجاء إدخال تاريخ صحيح",dateISO:"رجاء إدخال تاريخ صحيح (ISO)",number:"رجاء إدخال عدد بطريقة صحيحة",digits:"رجاء إدخال أرقام فقط",creditcard:"رجاء إدخال رقم بطاقة ائتمان صحيح",equalTo:"رجاء إدخال نفس القيمة",extension:"رجاء إدخال ملف بامتداد موافق عليه",maxlength:a.validator.format("الحد الأقصى لعدد الحروف هو {0}"),minlength:a.validator.format("الحد الأدنى لعدد الحروف هو {0}"),rangelength:a.validator.format("عدد الحروف يجب أن يكون بين {0} و {1}"),range:a.validator.format("رجاء إدخال عدد قيمته بين {0} و {1}"),max:a.validator.format("رجاء إدخال عدد أقل من أو يساوي {0}"),min:a.validator.format("رجاء إدخال عدد أكبر من أو يساوي {0}")}),a});
* Copyright (c) 2024 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"هذا الحقل إلزامي",remote:"يرجى تصحيح هذا الحقل للمتابعة",email:"رجاء إدخال عنوان بريد إلكتروني صحيح",url:"رجاء إدخال عنوان موقع إلكتروني صحيح",date:"رجاء إدخال تاريخ صحيح",dateISO:"رجاء إدخال تاريخ صحيح (ISO)",number:"رجاء إدخال عدد بطريقة صحيحة",digits:"رجاء إدخال أرقام فقط",creditcard:"رجاء إدخال رقم بطاقة ائتمان صحيح",equalTo:"رجاء إدخال نفس القيمة",extension:"رجاء إدخال ملف بامتداد موافق عليه",maxlength:a.validator.format("الحد الأقصى لعدد الحروف هو {0}"),minlength:a.validator.format("الحد الأدنى لعدد الحروف هو {0}"),rangelength:a.validator.format("عدد الحروف يجب أن يكون بين {0} و {1}"),range:a.validator.format("رجاء إدخال عدد قيمته بين {0} و {1}"),max:a.validator.format("رجاء إدخال عدد أقل من أو يساوي {0}"),min:a.validator.format("رجاء إدخال عدد أكبر من أو يساوي {0}"),step:a.validator.format("يرجى تقديم قيمة من مضاعفات {0}"),maxWords:a.validator.format("يرجى تقديم ما لا يزيد عن {0} كلمات"),minWords:a.validator.format("يرجى تقديم {0} كلمات على الأقل"),rangeWords:a.validator.format("يرجى تقديم ما بين {0} و{1} كلمة"),letterswithbasicpunc:"يرجى تقديم الحروف وعلامات الترقيم فقط",alphanumeric:"يرجى تقديم الحروف والأرقام والمسافات والتسطير فقط",lettersonly:"يرجى تقديم الحروف فقط",nowhitespace:"من فضلك لا تدخل المساحات البيضاء",ziprange:"يرجى تقديم الرمز البريدي بين 902xx-xxxx و905-xx-xxxx",integer:"يرجى تقديم رقم غير عشري موجب أو سالب",vinUS:"يرجى تقديم رقم تعريف المركبة (VIN)",dateITA:"يرجى تقديم تاريخ صالح",time:"يرجى تقديم وقت صالح بين 00:00 و23:59",phoneUS:"الرجاء تقديم رقم هاتف صالح",phoneUK:"الرجاء تقديم رقم هاتف صالح",mobileUK:"يرجى تقديم رقم هاتف محمول صالح",strippedminlength:a.validator.format("يرجى تقديم {0} حرفًا على الأقل"),email2:"يرجى تقديم عنوان بريد إلكتروني صالح",url2:"يرجى إدخال عنوان بريد إلكتروني صحيح",creditcardtypes:"يرجى تقديم رقم بطاقة ائتمان صالح",currency:"يرجى تقديم عملة صالحة",ipv4:"يرجى تقديم عنوان IP v4 صالح",ipv6:"يرجى تقديم عنوان IP v6 صالح",require_from_group:a.validator.format("يرجى تقديم ما لا يقل عن {0} من هذه الحقول"),nifES:"يرجى تقديم رقم TIN صالح",nieES:"يرجى تقديم رقم NIE صالح",cifES:"يرجى تقديم رقم CIF صالح",postalCodeCA:"يرجى تقديم رمز بريدي صالح",pattern:"التنسيق غير صالح"}),a});

4
aspnet-core/services/LY.MicroService.AuthServer/wwwroot/libs/jquery-validation/localization/messages_az.min.js

@ -1,4 +1,4 @@
/*! jQuery Validation Plugin - v1.20.0 - 10/10/2023
/*! jQuery Validation Plugin - v1.21.0 - 7/17/2024
* https://jqueryvalidation.org/
* Copyright (c) 2023 Jörn Zaefferer; Licensed MIT */
* Copyright (c) 2024 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Bu xana mütləq doldurulmalıdır.",remote:"Zəhmət olmasa, düzgün məna daxil edin.",email:"Zəhmət olmasa, düzgün elektron poçt daxil edin.",url:"Zəhmət olmasa, düzgün URL daxil edin.",date:"Zəhmət olmasa, düzgün tarix daxil edin.",dateISO:"Zəhmət olmasa, düzgün ISO formatlı tarix daxil edin.",number:"Zəhmət olmasa, düzgün rəqəm daxil edin.",digits:"Zəhmət olmasa, yalnız rəqəm daxil edin.",creditcard:"Zəhmət olmasa, düzgün kredit kart nömrəsini daxil edin.",equalTo:"Zəhmət olmasa, eyni mənanı bir daha daxil edin.",extension:"Zəhmət olmasa, düzgün genişlənməyə malik faylı seçin.",maxlength:a.validator.format("Zəhmət olmasa, {0} simvoldan çox olmayaraq daxil edin."),minlength:a.validator.format("Zəhmət olmasa, {0} simvoldan az olmayaraq daxil edin."),rangelength:a.validator.format("Zəhmət olmasa, {0} - {1} aralığında uzunluğa malik simvol daxil edin."),range:a.validator.format("Zəhmət olmasa, {0} - {1} aralığında rəqəm daxil edin."),max:a.validator.format("Zəhmət olmasa, {0} və ondan kiçik rəqəm daxil edin."),min:a.validator.format("Zəhmət olmasa, {0} və ondan böyük rəqəm daxil edin.")}),a});

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

Loading…
Cancel
Save