From 700a22423f7f6d165bc9f43aa5b6cbb002c7e10e Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 19 Mar 2019 14:18:23 +0300 Subject: [PATCH 001/116] Add TenantName to Audit logs #891 --- .../Volo/Abp/Auditing/AuditLogActionInfo.cs | 1 + .../Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs | 4 +++- .../Volo/Abp/Auditing/AuditingHelper.cs | 1 + .../Volo/Abp/Auditing/EntityChangeInfo.cs | 1 + .../Volo/Abp/Auditing/EntityPropertyChangeInfo.cs | 1 + .../Volo/Abp/AuditLogging/AuditLogConsts.cs | 2 ++ .../Volo/Abp/AuditLogging/AuditLog.cs | 9 ++++++--- 7 files changed, 15 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs index 067f4b2431..0959637ffb 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs @@ -5,6 +5,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.Auditing { + [Serializable] public class AuditLogActionInfo : IMultiTenant, IHasExtraProperties { public Guid? TenantId { get; set; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs index 79becb4ea0..d6b77313f0 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs @@ -7,7 +7,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.Auditing { - //TODO: Make serializable! + [Serializable] public class AuditLogInfo : IMultiTenant, IHasExtraProperties { public string ApplicationName { get; set; } @@ -18,6 +18,8 @@ namespace Volo.Abp.Auditing public Guid? TenantId { get; set; } + public string TenantName { get; set; } + public Guid? ImpersonatorUserId { get; set; } public Guid? ImpersonatorTenantId { get; set; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs index c584d322a0..9366c8cc03 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs @@ -92,6 +92,7 @@ namespace Volo.Abp.Auditing { ApplicationName = Options.ApplicationName, TenantId = CurrentTenant.Id, + TenantName = CurrentTenant. UserId = CurrentUser.Id, UserName = CurrentUser.UserName, ClientId = CurrentClient.Id, diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs index f0532e3009..c0dffa09fd 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs @@ -6,6 +6,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.Auditing { + [Serializable] public class EntityChangeInfo : IMultiTenant, IHasExtraProperties { public DateTime ChangeTime { get; set; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs index 53d45329c0..c1f53b1b70 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs @@ -3,6 +3,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.Auditing { + [Serializable] public class EntityPropertyChangeInfo : IMultiTenant { /// diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs index f6a73bb19a..1a592fc8b8 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs @@ -23,5 +23,7 @@ public const int MaxHttpMethodLength = 16; public const int MaxUserNameLength = 256; + + public const int MaxTenantNameLength = 64; } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs index 5640685b2e..38f32180cb 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs @@ -19,6 +19,8 @@ namespace Volo.Abp.AuditLogging public virtual Guid? TenantId { get; protected set; } + public virtual string TenantName { get; protected set; } + public virtual Guid? ImpersonatorUserId { get; protected set; } public virtual Guid? ImpersonatorTenantId { get; protected set; } @@ -31,9 +33,9 @@ namespace Volo.Abp.AuditLogging public virtual string ClientName { get; protected set; } - public string ClientId { get; set; } + public virtual string ClientId { get; set; } - public string CorrelationId { get; set; } + public virtual string CorrelationId { get; set; } public virtual string BrowserInfo { get; protected set; } @@ -59,8 +61,9 @@ namespace Volo.Abp.AuditLogging public AuditLog(IGuidGenerator guidGenerator, AuditLogInfo auditInfo) { Id = guidGenerator.Create(); - ApplicationName = auditInfo.ApplicationName; + ApplicationName = auditInfo.ApplicationName.Truncate(AuditLogConsts.MaxApplicationNameLength); TenantId = auditInfo.TenantId; + TenantName = auditInfo.TenantName.Truncate(AuditLogConsts.MaxTenantNameLength); UserId = auditInfo.UserId; UserName = auditInfo.UserName.Truncate(AuditLogConsts.MaxUserNameLength); ExecutionTime = auditInfo.ExecutionTime; From 4b747332274872dc0f288ac97f21425db17c2c00 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 28 Mar 2019 16:34:12 +0800 Subject: [PATCH 002/116] fix #928 Module and service templates add git ignore files. --- templates/module/.gitattributes | 1 + templates/module/.gitignore | 255 +++++++++++++++++++++++++++++++ templates/service/.gitattributes | 1 + templates/service/.gitignore | 255 +++++++++++++++++++++++++++++++ 4 files changed, 512 insertions(+) create mode 100644 templates/module/.gitattributes create mode 100644 templates/module/.gitignore create mode 100644 templates/service/.gitattributes create mode 100644 templates/service/.gitignore diff --git a/templates/module/.gitattributes b/templates/module/.gitattributes new file mode 100644 index 0000000000..c941e52669 --- /dev/null +++ b/templates/module/.gitattributes @@ -0,0 +1 @@ +**/wwwroot/libs/** linguist-vendored diff --git a/templates/module/.gitignore b/templates/module/.gitignore new file mode 100644 index 0000000000..1114901d9b --- /dev/null +++ b/templates/module/.gitignore @@ -0,0 +1,255 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# MyProjectName +src/MyCompanyName.MyProjectName.Web/Logs/logs.txt diff --git a/templates/service/.gitattributes b/templates/service/.gitattributes new file mode 100644 index 0000000000..c941e52669 --- /dev/null +++ b/templates/service/.gitattributes @@ -0,0 +1 @@ +**/wwwroot/libs/** linguist-vendored diff --git a/templates/service/.gitignore b/templates/service/.gitignore new file mode 100644 index 0000000000..1114901d9b --- /dev/null +++ b/templates/service/.gitignore @@ -0,0 +1,255 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# MyProjectName +src/MyCompanyName.MyProjectName.Web/Logs/logs.txt From 3eb76db2c659b2cce902c36b2350f67963401803 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 29 Mar 2019 10:47:26 +0800 Subject: [PATCH 003/116] fix #931 The dictionary configuration key uses the string name of the variable. --- .../Abp/Http/Client/RemoteServiceConfiguration.cs | 4 ++-- .../IdentityModel/IdentityClientConfiguration.cs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs index d6e5021f78..ae9a529bba 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs @@ -10,7 +10,7 @@ namespace Volo.Abp.Http.Client public string BaseUrl { get => this.GetOrDefault(nameof(BaseUrl)); - set => this[BaseUrl] = value; + set => this[nameof(BaseUrl)] = value; } /// @@ -19,7 +19,7 @@ namespace Volo.Abp.Http.Client public string Version { get => this.GetOrDefault(nameof(Version)); - set => this[Version] = value; + set => this[nameof(Version)] = value; } public RemoteServiceConfiguration() diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs index 520195e7ce..9e891ccafa 100644 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs +++ b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs @@ -12,7 +12,7 @@ namespace Volo.Abp.IdentityModel public string GrantType { get => this.GetOrDefault(nameof(GrantType)); - set => this[GrantType] = value; + set => this[nameof(GrantType)] = value; } /// @@ -21,7 +21,7 @@ namespace Volo.Abp.IdentityModel public string ClientId { get => this.GetOrDefault(nameof(ClientId)); - set => this[ClientId] = value; + set => this[nameof(ClientId)] = value; } /// @@ -30,7 +30,7 @@ namespace Volo.Abp.IdentityModel public string ClientSecret { get => this.GetOrDefault(nameof(ClientSecret)); - set => this[ClientSecret] = value; + set => this[nameof(ClientSecret)] = value; } /// @@ -40,7 +40,7 @@ namespace Volo.Abp.IdentityModel public string UserName { get => this.GetOrDefault(nameof(UserName)); - set => this[UserName] = value; + set => this[nameof(UserName)] = value; } /// @@ -50,7 +50,7 @@ namespace Volo.Abp.IdentityModel public string UserPassword { get => this.GetOrDefault(nameof(UserPassword)); - set => this[UserPassword] = value; + set => this[nameof(UserPassword)] = value; } /// @@ -59,7 +59,7 @@ namespace Volo.Abp.IdentityModel public string Authority { get => this.GetOrDefault(nameof(Authority)); - set => this[Authority] = value; + set => this[nameof(Authority)] = value; } /// @@ -68,7 +68,7 @@ namespace Volo.Abp.IdentityModel public string Scope { get => this.GetOrDefault(nameof(Scope)); - set => this[Scope] = value; + set => this[nameof(Scope)] = value; } public IdentityClientConfiguration() From 3d042733991cea2016eb3b49508fcd5421d10092 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 29 Mar 2019 09:36:20 +0300 Subject: [PATCH 004/116] Increment version to 0.15.0. --- common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.props b/common.props index 0e6a922473..dce50d895e 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 0.15.0 + 0.16.0 $(NoWarn);CS1591 https://abp.io/assets/abp_nupkg.png https://abp.io From b1087887b961c05ff9d8ddea84257a282afaf7ec Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 29 Mar 2019 10:20:19 +0300 Subject: [PATCH 005/116] Refactor PermissionChecker --- .../Abp/Authorization/Permissions/PermissionChecker.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs index d69ac81e78..dbc0fc147a 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs @@ -50,13 +50,10 @@ namespace Volo.Abp.Authorization.Permissions { Check.NotNull(name, nameof(name)); - var context = new PermissionValueCheckContext( - PermissionDefinitionManager.Get(name), - claimsPrincipal - ); - var isGranted = false; + var permission = PermissionDefinitionManager.Get(name); + var context = new PermissionValueCheckContext(permission, claimsPrincipal); foreach (var provider in ValueProviders) { if (context.Permission.Providers.Any() && From 65a2d89b18f0876c6f5458136a57c6250d31ae7e Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 29 Mar 2019 10:38:20 +0300 Subject: [PATCH 006/116] Use new IDataSeeder. --- .../AbpWebSiteWebModule.cs | 16 ++------------- .../BloggingTestAppModule.cs | 14 ++----------- .../app/Volo.DocsTestApp/DocsTestAppModule.cs | 16 ++------------- .../AuthServer.Host/AuthServerHostModule.cs | 16 +++------------ .../DemoAppModule.cs | 14 ++----------- .../MyProjectNameWebModule.cs | 20 ++++--------------- .../MyProjectNameTestDataBuilder.cs | 9 +++++---- 7 files changed, 20 insertions(+), 85 deletions(-) diff --git a/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs b/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs index 8031142779..81105447fc 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs +++ b/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -14,7 +13,6 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theming; -using Volo.Abp.Authorization.Permissions; using Volo.Abp.Autofac; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -182,18 +180,8 @@ namespace Volo.AbpWebSite AsyncHelper.RunSync(async () => { await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - "1q2w3E*" - ); - - await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - RolePermissionValueProvider.ProviderName, - "admin", - IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll()) - ); + .GetRequiredService() + .SeedAsync(); }); } } diff --git a/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs b/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs index aec8044d3b..248d1433cc 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs +++ b/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs @@ -159,18 +159,8 @@ namespace Volo.BloggingTestApp AsyncHelper.RunSync(async () => { await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - "1q2w3E*" - ); - - await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - RolePermissionValueProvider.ProviderName, - "admin", - IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll()) - ); + .GetRequiredService() + .SeedAsync(); }); } } diff --git a/modules/docs/app/Volo.DocsTestApp/DocsTestAppModule.cs b/modules/docs/app/Volo.DocsTestApp/DocsTestAppModule.cs index 46835ccde9..9e25b70345 100644 --- a/modules/docs/app/Volo.DocsTestApp/DocsTestAppModule.cs +++ b/modules/docs/app/Volo.DocsTestApp/DocsTestAppModule.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Localization; @@ -16,7 +15,6 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theming; -using Volo.Abp.Authorization.Permissions; using Volo.Abp.Autofac; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -136,19 +134,9 @@ namespace Volo.DocsTestApp { AsyncHelper.RunSync(async () => { - await context.ServiceProvider - .GetRequiredService() - .SeedAsync( - "1q2w3E*" - ); - await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - RolePermissionValueProvider.ProviderName, - "admin", - IdentityPermissions.GetAll().Union(DocsAdminPermissions.GetAll()) - ); + .GetRequiredService() + .SeedAsync(); }); } } diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServerHostModule.cs b/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServerHostModule.cs index 56d6c4900c..19baec6988 100644 --- a/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServerHostModule.cs +++ b/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServerHostModule.cs @@ -11,6 +11,7 @@ using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.Authorization.Permissions; using Volo.Abp.EventBus.RabbitMq; using Volo.Abp.Autofac; +using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.SqlServer; using Volo.Abp.Identity; @@ -93,21 +94,10 @@ namespace AuthServer.Host AsyncHelper.RunSync(async () => { await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - adminUserPassword: "1q2w3E*" - ); - - await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - RolePermissionValueProvider.ProviderName, - "admin", - IdentityPermissions.GetAll() - ); + .GetRequiredService() + .SeedAsync(); }); } - } } } diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppModule.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppModule.cs index 9d1560acd9..4bf1ab94f8 100644 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppModule.cs +++ b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppModule.cs @@ -123,18 +123,8 @@ namespace MyCompanyName.MyProjectName.DemoApp AsyncHelper.RunSync(async () => { await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - "1q2w3E*" - ); - - await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - RolePermissionValueProvider.ProviderName, - "admin", - IdentityPermissions.GetAll().Union(MyProjectNamePermissions.GetAll()) - ); + .GetRequiredService() + .SeedAsync(); }); } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs index a18f4bb458..08ccecdf38 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs @@ -20,6 +20,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; +using Volo.Abp.Data; using Volo.Abp.Identity; using Volo.Abp.Identity.Web; using Volo.Abp.Localization; @@ -195,22 +196,9 @@ namespace MyCompanyName.MyProjectName { AsyncHelper.RunSync(async () => { - var identitySeedResult = await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - "1q2w3E*" - ); - - if (identitySeedResult.CreatedAdminRole) - { - await scope.ServiceProvider - .GetRequiredService() - .SeedAsync( - RolePermissionValueProvider.ProviderName, - "admin", - IdentityPermissions.GetAll().Union(MyProjectNamePermissions.GetAll()) - ); - } + await scope.ServiceProvider + .GetRequiredService() + .SeedAsync(); }); } } diff --git a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyProjectNameTestDataBuilder.cs b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyProjectNameTestDataBuilder.cs index 4096272846..fa3c0be470 100644 --- a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyProjectNameTestDataBuilder.cs +++ b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyProjectNameTestDataBuilder.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; using Volo.Abp.Threading; @@ -7,11 +8,11 @@ namespace MyCompanyName.MyProjectName { public class MyProjectNameTestDataBuilder : ITransientDependency { - private readonly IIdentityDataSeeder _identityDataSeeder; + private readonly IDataSeeder _dataSeeder; - public MyProjectNameTestDataBuilder(IIdentityDataSeeder identityDataSeeder) + public MyProjectNameTestDataBuilder(IDataSeeder dataSeeder) { - _identityDataSeeder = identityDataSeeder; + _dataSeeder = dataSeeder; } public void Build() @@ -21,7 +22,7 @@ namespace MyCompanyName.MyProjectName public async Task BuildInternalAsync() { - await _identityDataSeeder.SeedAsync("1q2w3E*"); + await _dataSeeder.SeedAsync(); } } } \ No newline at end of file From 719a8db868f655b0c2d7d2b2ed362b10d47a5754 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 31 Mar 2019 16:37:56 +0800 Subject: [PATCH 007/116] fix #934 Correct the wrong module name in the document and powershell. --- docs/en/Multi-Tenancy.md | 14 +++++++------- docs/zh-Hans/Multi-Tenancy.md | 14 +++++++------- nupkg/common.ps1 | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md index d8c52f38fe..118fea5c19 100644 --- a/docs/en/Multi-Tenancy.md +++ b/docs/en/Multi-Tenancy.md @@ -6,17 +6,17 @@ Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) multi-tenancy as > Software **Multi-tenancy** refers to a software **architecture** in which a **single instance** of a software runs on a server and serves **multiple tenants**. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a **dedicated share of the instance including its data**, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants. -### Volo.Abp.MultiTenancy.Abstractions Package +### Volo.Abp.MultiTenancy Package -Volo.Abp.MultiTenancy.Abstractions package defines fundamental interfaces to make your code "multi-tenancy ready". So, install it to your project using the package manager console (PMC): +Volo.Abp.MultiTenancy package defines fundamental interfaces to make your code "multi-tenancy ready". So, install it to your project using the package manager console (PMC): ```` -Install-Package Volo.Abp.MultiTenancy.Abstractions +Install-Package Volo.Abp.MultiTenancy ```` > This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. -Then you can add **AbpMultiTenancyAbstractionsModule** dependency to your module: +Then you can add **AbpMultiTenancyModule** dependency to your module: ````C# using Volo.Abp.Modularity; @@ -24,7 +24,7 @@ using Volo.Abp.MultiTenancy; namespace MyCompany.MyProject { - [DependsOn(typeof(AbpMultiTenancyAbstractionsModule))] + [DependsOn(typeof(AbpMultiTenancyModule))] public class MyModule : AbpModule { //... @@ -98,7 +98,7 @@ Volo.Abp.MultiTenancy is the actual package that makes your application multi-te Install-Package Volo.Abp.MultiTenancy ```` -Then you can add **AbpMultiTenancyAbstractionsModule** dependency to your module: +Then you can add **AbpMultiTenancyModule** dependency to your module: ````C# using Volo.Abp.Modularity; @@ -114,7 +114,7 @@ namespace MyCompany.MyProject } ```` -> If you add AbpMultiTenancyModule dependency to your module, then you don't need to add AbpMultiTenancyAbstractionsModule dependency separately since AbpMultiTenancyModule already depends on it. +> If you add AbpMultiTenancyModule dependency to your module, then you don't need to add AbpMultiTenancyModule dependency separately since AbpMultiTenancyModule already depends on it. #### Determining Current Tenant diff --git a/docs/zh-Hans/Multi-Tenancy.md b/docs/zh-Hans/Multi-Tenancy.md index 11b729e79f..6364428ec1 100644 --- a/docs/zh-Hans/Multi-Tenancy.md +++ b/docs/zh-Hans/Multi-Tenancy.md @@ -6,17 +6,17 @@ ABP的多租户模块提供了创建多租户应用程序的基本功能. > 软件多租户技术指的是一种软件架构,这种架构可以使用软件的单实例运行并为多个租户提供服务.租户是通过软件实例的特定权限共享通用访问的一组用户.使用多租户架构,软件应用为每个租户提供实例的专用共享,包括实例的数据、配置、用户管理、租户的私有功能和非功能属性.多租户与多实例架构形成对比,将软件实例的行为根据不同的租户分割开来. -### Volo.Abp.MultiTenancy.Abstractions +### Volo.Abp.MultiTenancy -Volo.Abp.MultiTenancy.Abstractions定义了一些基础接口让你的代码"multi-tenancy ready",使用包管理器控制台(PMC)将它安装到你的项目中: +Volo.Abp.MultiTenancy"multi-tenancy ready",使用包管理器控制台(PMC)将它安装到你的项目中: ```` -Install-Package Volo.Abp.MultiTenancy.Abstractions +Install-Package Volo.Abp.MultiTenancy ```` > 这个包默认安装在了快速启动模板中.所以,大多数情况下,你不需要手动安装它. -然后你可以添加 **AbpMultiTenancyAbstractionsModule** 依赖到你的模块: +然后你可以添加 **AbpMultiTenancyModule** 依赖到你的模块: ````C# using Volo.Abp.Modularity; @@ -24,7 +24,7 @@ using Volo.Abp.MultiTenancy; namespace MyCompany.MyProject { - [DependsOn(typeof(AbpMultiTenancyAbstractionsModule))] + [DependsOn(typeof(AbpMultiTenancyModule))] public class MyModule : AbpModule { //... @@ -98,7 +98,7 @@ Volo.Abp.MultiTenancy 才是让你的程序实现多租户的真正的包.使用 Install-Package Volo.Abp.MultiTenancy ```` -然后添加 **AbpMultiTenancyAbstractionsModule** 依赖到你的模块中: +然后添加 **AbpMultiTenancyModule** 依赖到你的模块中: ````C# using Volo.Abp.Modularity; @@ -114,7 +114,7 @@ namespace MyCompany.MyProject } ```` -> 如果你添加了AbpMultiTenancyModule依赖,就不需要再另外添加AbpMultiTenancyAbstractionsModule依赖了,因为AbpMultiTenancyModule已经依赖它了. +> 如果你添加了AbpMultiTenancyModule依赖,就不需要再另外添加AbpMultiTenancyModule依赖了,因为AbpMultiTenancyModule已经依赖它了. #### 确定当前租户 diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index d7ce2ce572..89348dc95e 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -74,7 +74,7 @@ $projects = ( "framework/src/Volo.Abp.Localization.Abstractions", "framework/src/Volo.Abp.MemoryDb", "framework/src/Volo.Abp.MongoDB", - "framework/src/Volo.Abp.MultiTenancy.Abstractions", + "framework/src/Volo.Abp.MultiTenancy", "framework/src/Volo.Abp.ObjectMapping", "framework/src/Volo.Abp.RabbitMQ", "framework/src/Volo.Abp.Security", From acf84e5c402efa6ff5069e2f3e9f85a6f870c2b4 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Sun, 31 Mar 2019 18:09:01 +0300 Subject: [PATCH 008/116] Respect to multitenancy side while getting & setting permissions. --- .../MultiTenancy/CurrentTenantExtensions.cs | 7 ++++ .../PermissionAppService.cs | 15 +++---- .../PermissionDataSeedContributor.cs | 12 ++++-- .../PermissionManagement/PermissionManager.cs | 40 ++++++++++++++++--- 4 files changed, 59 insertions(+), 15 deletions(-) diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantExtensions.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantExtensions.cs index 67684c030f..6a8f862087 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantExtensions.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantExtensions.cs @@ -16,5 +16,12 @@ namespace Volo.Abp.MultiTenancy return currentTenant.Id.Value; } + + public static MultiTenancySides GetMultiTenancySide(this ICurrentTenant currentTenant) + { + return currentTenant.Id.HasValue + ? MultiTenancySides.Tenant + : MultiTenancySides.Host; + } } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs index 560c5b87f1..2155420600 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using Volo.Abp.Application.Services; using Volo.Abp.Authorization.Permissions; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.PermissionManagement { @@ -41,6 +42,8 @@ namespace Volo.Abp.PermissionManagement Groups = new List() }; + var multiTenancySide = CurrentTenant.GetMultiTenancySide(); + foreach (var group in _permissionDefinitionManager.GetGroups()) { var groupDto = new PermissionGroupDto @@ -57,6 +60,11 @@ namespace Volo.Abp.PermissionManagement continue; } + if (!permission.MultiTenancySide.HasFlag(multiTenancySide)) + { + continue; + } + var grantInfoDto = new PermissionGrantInfoDto { Name = permission.Name, @@ -97,13 +105,6 @@ namespace Volo.Abp.PermissionManagement foreach (var permissionDto in input.Permissions) { - var permissionDefinition = _permissionDefinitionManager.Get(permissionDto.Name); - if (permissionDefinition.Providers.Any() && - !permissionDefinition.Providers.Contains(providerName)) - { - throw new ApplicationException($"The permission named '{permissionDto.Name}' has not compatible with the provider named '{providerName}'"); - } - await _permissionManager.SetAsync(permissionDto.Name, providerName, providerKey, permissionDto.IsGranted); } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs index 203082828b..719dc654b8 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs @@ -3,28 +3,34 @@ using System.Threading.Tasks; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.PermissionManagement { public class PermissionDataSeedContributor : IDataSeedContributor, ITransientDependency { + protected ICurrentTenant CurrentTenant { get; } + protected IPermissionDefinitionManager PermissionDefinitionManager { get; } protected IPermissionDataSeeder PermissionDataSeeder { get; } public PermissionDataSeedContributor( IPermissionDefinitionManager permissionDefinitionManager, - IPermissionDataSeeder permissionDataSeeder) + IPermissionDataSeeder permissionDataSeeder, + ICurrentTenant currentTenant) { PermissionDefinitionManager = permissionDefinitionManager; PermissionDataSeeder = permissionDataSeeder; + CurrentTenant = currentTenant; } - public Task SeedAsync(DataSeedContext context) + public virtual Task SeedAsync(DataSeedContext context) { + var multiTenancySide = CurrentTenant.GetMultiTenancySide(); var permissionNames = PermissionDefinitionManager .GetPermissions() + .Where(p => p.MultiTenancySide.HasFlag(multiTenancySide)) .Select(p => p.Name) - //TODO: Filter host/tenant permissions! .ToArray(); return PermissionDataSeeder.SeedAsync( diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs index 18dff9f825..f8825c6e81 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Options; using Volo.Abp.Authorization.Permissions; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.PermissionManagement { @@ -18,6 +19,8 @@ namespace Volo.Abp.PermissionManagement protected IGuidGenerator GuidGenerator { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IReadOnlyList ManagementProviders => _lazyProviders.Value; protected PermissionManagementOptions Options { get; } @@ -29,9 +32,11 @@ namespace Volo.Abp.PermissionManagement IPermissionGrantRepository permissionGrantRepository, IServiceProvider serviceProvider, IGuidGenerator guidGenerator, - IOptions options) + IOptions options, + ICurrentTenant currentTenant) { GuidGenerator = guidGenerator; + CurrentTenant = currentTenant; PermissionGrantRepository = permissionGrantRepository; PermissionDefinitionManager = permissionDefinitionManager; Options = options.Value; @@ -64,7 +69,21 @@ namespace Volo.Abp.PermissionManagement public async Task SetAsync(string permissionName, string providerName, string providerKey, bool isGranted) { - var currentGrantInfo = await GetAsync(permissionName, providerName, providerKey); + var permission = PermissionDefinitionManager.Get(permissionName); + + if (permission.Providers.Any() && !permission.Providers.Contains(providerName)) + { + //TODO: BusinessException + throw new ApplicationException($"The permission named '{permission.Name}' has not compatible with the provider named '{providerName}'"); + } + + if (!permission.MultiTenancySide.HasFlag(CurrentTenant.GetMultiTenancySide())) + { + //TODO: BusinessException + throw new ApplicationException($"The permission named '{permission.Name}' has multitenancy side '{permission.MultiTenancySide}' which is not compatible with the current multitenancy side '{CurrentTenant.GetMultiTenancySide()}'"); + } + + var currentGrantInfo = await GetInternalAsync(permission, providerName, providerKey); if (currentGrantInfo.IsGranted == isGranted) { return; @@ -73,19 +92,30 @@ namespace Volo.Abp.PermissionManagement var provider = ManagementProviders.FirstOrDefault(m => m.Name == providerName); if (provider == null) { + //TODO: BusinessException throw new AbpException("Unknown permission management provider: " + providerName); } await provider.SetAsync(permissionName, providerKey, isGranted); } - protected virtual async Task GetInternalAsync(PermissionDefinition permissionDefinition, string providerName, string providerKey) + protected virtual async Task GetInternalAsync(PermissionDefinition permission, string providerName, string providerKey) { - var result = new PermissionWithGrantedProviders(permissionDefinition.Name, false); + var result = new PermissionWithGrantedProviders(permission.Name, false); + + if (!permission.MultiTenancySide.HasFlag(CurrentTenant.GetMultiTenancySide())) + { + return result; + } + + if (permission.Providers.Any() && !permission.Providers.Contains(providerName)) + { + return result; + } foreach (var provider in ManagementProviders) { - var providerResult = await provider.CheckAsync(permissionDefinition.Name, providerName, providerKey); + var providerResult = await provider.CheckAsync(permission.Name, providerName, providerKey); if (providerResult.IsGranted) { result.IsGranted = true; From ccf8415476a57d64072e038b309a8034a1b83416 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Sun, 31 Mar 2019 21:41:21 +0300 Subject: [PATCH 009/116] Add length parameters to Check methods. --- framework/src/Volo.Abp.Core/Volo/Abp/Check.cs | 97 +++++++++++++++++-- 1 file changed, 91 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs index 2a814a585a..6d314d0685 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs @@ -1,7 +1,7 @@ -using System; +using JetBrains.Annotations; +using System; using System.Collections.Generic; using System.Diagnostics; -using JetBrains.Annotations; namespace Volo.Abp { @@ -9,7 +9,9 @@ namespace Volo.Abp public static class Check { [ContractAnnotation("value:null => halt")] - public static T NotNull(T value, [InvokerParameterName] [NotNull] string parameterName) + public static T NotNull( + T value, + [InvokerParameterName] [NotNull] string parameterName) { if (value == null) { @@ -20,7 +22,10 @@ namespace Volo.Abp } [ContractAnnotation("value:null => halt")] - public static T NotNull(T value, [InvokerParameterName] [NotNull] string parameterName, string message) + public static T NotNull( + T value, + [InvokerParameterName] [NotNull] string parameterName, + string message) { if (value == null) { @@ -31,24 +36,77 @@ namespace Volo.Abp } [ContractAnnotation("value:null => halt")] - public static string NotNullOrWhiteSpace(string value, [InvokerParameterName] [NotNull] string parameterName) + public static string NotNull( + string value, + [InvokerParameterName] [NotNull] string parameterName, + int maxLength = int.MaxValue, + int minLength = 0) + { + if (value == null) + { + throw new ArgumentException($"{parameterName} can not be null!", parameterName); + } + + if (value.Length > maxLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName); + } + + if (minLength > 0 && value.Length < minLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static string NotNullOrWhiteSpace( + string value, + [InvokerParameterName] [NotNull] string parameterName, + int maxLength = int.MaxValue, + int minLength = 0) { if (value.IsNullOrWhiteSpace()) { throw new ArgumentException($"{parameterName} can not be null, empty or white space!", parameterName); } + if (value.Length > maxLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName); + } + + if (minLength > 0 && value.Length < minLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName); + } + return value; } [ContractAnnotation("value:null => halt")] - public static string NotNullOrEmpty(string value, [InvokerParameterName] [NotNull] string parameterName) + public static string NotNullOrEmpty( + string value, + [InvokerParameterName] [NotNull] string parameterName, + int maxLength = int.MaxValue, + int minLength = 0) { if (value.IsNullOrEmpty()) { throw new ArgumentException($"{parameterName} can not be null or empty!", parameterName); } + if (value.Length > maxLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName); + } + + if (minLength > 0 && value.Length < minLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName); + } + return value; } @@ -62,5 +120,32 @@ namespace Volo.Abp return value; } + + public static string Length( + string value, + [InvokerParameterName] [NotNull] string parameterName, + int maxLength, + int minLength = 0) + { + if (minLength > 0) + { + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentException(parameterName + " can not be null or empty!", parameterName); + } + + if (value.Length < minLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName); + } + } + + if (value != null && value.Length > maxLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName); + } + + return value; + } } } From 6182ed5e52abd16a240432803ad6cd77ff448cff Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Sun, 31 Mar 2019 21:42:12 +0300 Subject: [PATCH 010/116] Make Tenant full audited. --- .../TenantConnectionStringConsts.cs | 2 +- .../Volo/Abp/TenantManagement/Tenant.cs | 13 ++++--------- .../Abp/TenantManagement/TenantConnectionString.cs | 14 ++++++++------ ...ntManagementDbContextModelCreatingExtensions.cs | 2 ++ 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs index 526d12b7d6..4cdf8dee2a 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs @@ -2,7 +2,7 @@ { public static class TenantConnectionStringConsts { - public const int MaxNameLength = 128; + public const int MaxNameLength = 64; public const int MaxValueLength = 1024; } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/Tenant.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/Tenant.cs index d9da76e254..74b9a7ebc0 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/Tenant.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/Tenant.cs @@ -2,12 +2,11 @@ using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; -using Volo.Abp.Data; -using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Entities.Auditing; namespace Volo.Abp.TenantManagement { - public class Tenant : AggregateRoot + public class Tenant : FullAuditedAggregateRoot { public virtual string Name { get; protected set; } @@ -20,10 +19,8 @@ namespace Volo.Abp.TenantManagement protected internal Tenant(Guid id, [NotNull] string name) { - Check.NotNull(name, nameof(name)); - Id = id; - Name = name; + SetName(name); ConnectionStrings = new List(); ExtraProperties = new Dictionary(); @@ -43,9 +40,7 @@ namespace Volo.Abp.TenantManagement internal void SetName([NotNull] string name) { - Check.NotNull(name, nameof(name)); - - Name = name; + Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConsts.MaxNameLength); } } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantConnectionString.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantConnectionString.cs index daa8832110..71fea8c50c 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantConnectionString.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantConnectionString.cs @@ -19,17 +19,19 @@ namespace Volo.Abp.TenantManagement public TenantConnectionString(Guid tenantId, [NotNull] string name, [NotNull] string value) { - Check.NotNull(name, nameof(name)); - Check.NotNull(value, nameof(value)); - TenantId = tenantId; - Name = name; - Value = value; + Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConnectionStringConsts.MaxNameLength); + SetValue(value); + } + + public virtual void SetValue([NotNull] string value) + { + Value = Check.NotNullOrWhiteSpace(value, nameof(value), TenantConnectionStringConsts.MaxValueLength); } public override object[] GetKeys() { - return new object[] { TenantId, Name, Value }; + return new object[] { TenantId, Name }; } } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs index 0022ee8475..0c5b20a638 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs @@ -22,7 +22,9 @@ namespace Volo.Abp.TenantManagement.EntityFrameworkCore { b.ToTable(tablePrefix + "Tenants", schema); + b.ConfigureFullAudited(); b.ConfigureExtraProperties(); + b.ConfigureConcurrencyStamp(); b.Property(t => t.Name).IsRequired().HasMaxLength(TenantConsts.MaxNameLength); From d6c3dfdd34122db0c0d6dd2d288126196fa423d1 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Sun, 31 Mar 2019 22:46:52 +0300 Subject: [PATCH 011/116] Set TenantName --- .../src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs index 9366c8cc03..ba061c5723 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs @@ -92,7 +92,7 @@ namespace Volo.Abp.Auditing { ApplicationName = Options.ApplicationName, TenantId = CurrentTenant.Id, - TenantName = CurrentTenant. + TenantName = CurrentTenant.Name, UserId = CurrentUser.Id, UserName = CurrentUser.UserName, ClientId = CurrentClient.Id, From fd92b7d4f749d79c573b291151e38d2a52121e27 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Mon, 1 Apr 2019 10:29:40 +0300 Subject: [PATCH 012/116] Decrease version to 0.15 temporarily to fix #938. --- common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.props b/common.props index dce50d895e..0e6a922473 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 0.16.0 + 0.15.0 $(NoWarn);CS1591 https://abp.io/assets/abp_nupkg.png https://abp.io From 9b73ab83b435915c8377b810b1c61f8560bd5f5f Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 1 Apr 2019 14:55:34 +0300 Subject: [PATCH 013/116] closes #941 - Invalid selector updated --- .../docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js index 24bd4e95ba..32ce1933a7 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js @@ -3,7 +3,7 @@ $(function () { var initNavigationFilter = function (navigationContainerId) { - + var $navigation = $("#" + navigationContainerId); var getShownDocumentLinks = function () { @@ -57,7 +57,7 @@ }); }; - $(".docs-page .docs-filter input[type='search']").keyup(function (e) { + $(".docs-page .docs-tree-list input[type='search']").keyup(function (e) { filterDocumentItems(e.target.value); if (e.key === "Enter") { From a4c0d64f19fec6823000355f5dee96da566e8088 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Mon, 1 Apr 2019 15:12:27 +0300 Subject: [PATCH 014/116] Add CurrentTenant.Id to dynamic http client. --- .../Volo.Abp.Http.Client.csproj | 1 + .../Volo/Abp/Http/Client/AbpHttpClientModule.cs | 4 +++- .../DynamicProxying/DynamicHttpProxyInterceptor.cs | 13 ++++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj b/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj index a93dc4fcb9..f6afd53f1d 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj +++ b/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj @@ -20,6 +20,7 @@ + diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs index f9240bd4a8..234a560e83 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Castle; using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; namespace Volo.Abp.Http.Client @@ -8,7 +9,8 @@ namespace Volo.Abp.Http.Client [DependsOn( typeof(AbpHttpModule), typeof(AbpCastleCoreModule), - typeof(AbpThreadingModule) + typeof(AbpThreadingModule), + typeof(AbpMultiTenancyModule) )] public class AbpHttpClientModule : AbpModule { diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index 21f27cce49..936bcd5fd3 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -16,6 +16,7 @@ using Volo.Abp.Http.Client.Authentication; using Volo.Abp.Http.Modeling; using Volo.Abp.Http.ProxyScripting.Generators; using Volo.Abp.Json; +using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; using Volo.Abp.Threading; using Volo.Abp.Tracing; @@ -29,6 +30,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying protected ICancellationTokenProvider CancellationTokenProvider { get; } protected ICorrelationIdProvider CorrelationIdProvider { get; } + protected ICurrentTenant CurrentTenant { get; } protected CorrelationIdOptions CorrelationIdOptions { get; } protected IDynamicProxyHttpClientFactory HttpClientFactory { get; } protected IApiDescriptionFinder ApiDescriptionFinder { get; } @@ -55,10 +57,12 @@ namespace Volo.Abp.Http.Client.DynamicProxying IRemoteServiceHttpClientAuthenticator clientAuthenticator, ICancellationTokenProvider cancellationTokenProvider, ICorrelationIdProvider correlationIdProvider, - IOptions correlationIdOptions) + IOptions correlationIdOptions, + ICurrentTenant currentTenant) { CancellationTokenProvider = cancellationTokenProvider; CorrelationIdProvider = correlationIdProvider; + CurrentTenant = currentTenant; CorrelationIdOptions = correlationIdOptions.Value; HttpClientFactory = httpClientFactory; ApiDescriptionFinder = apiDescriptionFinder; @@ -213,6 +217,13 @@ namespace Volo.Abp.Http.Client.DynamicProxying //CorrelationId requestMessage.Headers.Add(CorrelationIdOptions.HttpHeaderName, CorrelationIdProvider.Get()); + //TenantId + if (CurrentTenant.Id.HasValue) + { + //TODO: Use AspNetCoreMultiTenancyOptions to get the key + requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString()); + } + //TODO: Is that the way we want? Couldn't send the culture (not ui culture) requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(CultureInfo.CurrentUICulture.Name)); } From 1d79b95a3889f6f89c89077b2046084afa73ae28 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Mon, 1 Apr 2019 16:21:19 +0300 Subject: [PATCH 015/116] Resolved #890: Handle audit log if there are tenant switches in the same request. --- .../Mvc/Auditing/AbpAuditActionFilter.cs | 1 + .../Volo/Abp/Auditing/AuditLogActionInfo.cs | 5 +- .../Volo/Abp/Auditing/AuditLogInfo.cs | 3 +- .../Volo/Abp/Auditing/AuditingHelper.cs | 14 +++- .../Volo/Abp/Auditing/AuditingInterceptor.cs | 10 ++- .../Volo/Abp/Auditing/EntityChangeInfo.cs | 12 ++- .../Abp/Auditing/EntityPropertyChangeInfo.cs | 5 +- .../Volo/Abp/Auditing/IAuditingHelper.cs | 14 +++- .../EntityHistory/EntityHistoryHelper.cs | 5 +- .../Volo/Abp/AuditLogging/AuditLog.cs | 32 ++++++-- .../Volo/Abp/AuditLogging/AuditLogAction.cs | 4 +- .../Volo/Abp/AuditLogging/EntityChange.cs | 23 +++++- .../Abp/AuditLogging/EntityPropertyChange.cs | 8 +- .../AuditLogging/AuditStore_Basic_Tests.cs | 1 - .../AuditLogging/MultiTenantAuditLog_Tests.cs | 76 +++++++++++++++++++ 15 files changed, 175 insertions(+), 38 deletions(-) create mode 100644 modules/audit-logging/test/Volo.Abp.AuditLogging.Tests/Volo/Abp/AuditLogging/MultiTenantAuditLog_Tests.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs index 6d31ba6542..33932137cb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs @@ -92,6 +92,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Auditing auditLog = auditLogScope.Log; auditLogAction = _auditingHelper.CreateAuditLogAction( + auditLog, context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(), context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo, context.ActionArguments diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs index 0959637ffb..d1b3bcfff9 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; using Volo.Abp.Data; -using Volo.Abp.MultiTenancy; namespace Volo.Abp.Auditing { [Serializable] - public class AuditLogActionInfo : IMultiTenant, IHasExtraProperties + public class AuditLogActionInfo : IHasExtraProperties { - public Guid? TenantId { get; set; } - public string ServiceName { get; set; } public string MethodName { get; set; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs index d6b77313f0..f780db924a 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs @@ -3,12 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Volo.Abp.Data; -using Volo.Abp.MultiTenancy; namespace Volo.Abp.Auditing { [Serializable] - public class AuditLogInfo : IMultiTenant, IHasExtraProperties + public class AuditLogInfo : IHasExtraProperties { public string ApplicationName { get; set; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs index ba061c5723..b333e07bf9 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs @@ -107,12 +107,20 @@ namespace Volo.Abp.Auditing return auditInfo; } - public virtual AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, object[] arguments) + public virtual AuditLogActionInfo CreateAuditLogAction( + AuditLogInfo auditLog, + Type type, + MethodInfo method, + object[] arguments) { - return CreateAuditLogAction(type, method, CreateArgumentsDictionary(method, arguments)); + return CreateAuditLogAction(auditLog, type, method, CreateArgumentsDictionary(method, arguments)); } - public virtual AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, IDictionary arguments) + public virtual AuditLogActionInfo CreateAuditLogAction( + AuditLogInfo auditLog, + Type type, + MethodInfo method, + IDictionary arguments) { var actionInfo = new AuditLogActionInfo { diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs index 0db0b7bb98..0ccfb92ffa 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs @@ -72,7 +72,10 @@ namespace Volo.Abp.Auditing } } - protected virtual bool ShouldIntercept(IAbpMethodInvocation invocation, out AuditLogInfo auditLog, out AuditLogActionInfo auditLogAction) + protected virtual bool ShouldIntercept( + IAbpMethodInvocation invocation, + out AuditLogInfo auditLog, + out AuditLogActionInfo auditLogAction) { auditLog = null; auditLogAction = null; @@ -95,7 +98,10 @@ namespace Volo.Abp.Auditing auditLog = auditLogScope.Log; auditLogAction = _auditingHelper.CreateAuditLogAction( - invocation.TargetObject.GetType(), invocation.Method, invocation.Arguments + auditLog, + invocation.TargetObject.GetType(), + invocation.Method, + invocation.Arguments ); return true; diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs index c0dffa09fd..5bf45f1375 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs @@ -2,23 +2,27 @@ using System.Collections.Generic; using System.Linq; using Volo.Abp.Data; -using Volo.Abp.MultiTenancy; namespace Volo.Abp.Auditing { [Serializable] - public class EntityChangeInfo : IMultiTenant, IHasExtraProperties + public class EntityChangeInfo : IHasExtraProperties { public DateTime ChangeTime { get; set; } public EntityChangeType ChangeType { get; set; } + /// + /// TenantId of the related entity. + /// This is not the TenantId of the audit log entry. + /// There can be multiple tenant data changes in a single audit log entry. + /// + public Guid? EntityTenantId { get; set; } + public string EntityId { get; set; } public string EntityTypeFullName { get; set; } - public Guid? TenantId { get; set; } - public List PropertyChanges { get; set; } public Dictionary ExtraProperties { get; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs index c1f53b1b70..a7a15408b4 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs @@ -1,10 +1,9 @@ using System; -using Volo.Abp.MultiTenancy; namespace Volo.Abp.Auditing { [Serializable] - public class EntityPropertyChangeInfo : IMultiTenant + public class EntityPropertyChangeInfo { /// /// Maximum length of property. @@ -24,8 +23,6 @@ namespace Volo.Abp.Auditing /// public const int MaxPropertyTypeFullNameLength = 192; - public Guid? TenantId { get; set; } - public virtual string NewValue { get; set; } public virtual string OriginalValue { get; set; } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs index bdc3aadff0..e6992491b8 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs @@ -11,8 +11,18 @@ namespace Volo.Abp.Auditing AuditLogInfo CreateAuditLogInfo(); - AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, object[] arguments); + AuditLogActionInfo CreateAuditLogAction( + AuditLogInfo auditLog, + Type type, + MethodInfo method, + object[] arguments + ); - AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, IDictionary arguments); + AuditLogActionInfo CreateAuditLogAction( + AuditLogInfo auditLog, + Type type, + MethodInfo method, + IDictionary arguments + ); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs index b50afd3b2a..d28468f306 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs @@ -103,7 +103,7 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory EntityId = entityId, EntityTypeFullName = entityType.FullName, PropertyChanges = GetPropertyChanges(entityEntry), - TenantId = GetTenantId(entity) + EntityTenantId = GetTenantId(entity) }; return entityChange; @@ -171,8 +171,7 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory NewValue = isDeleted ? null : JsonSerializer.Serialize(propertyEntry.CurrentValue).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength), OriginalValue = isCreated ? null : JsonSerializer.Serialize(propertyEntry.OriginalValue).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength), PropertyName = property.Name, - PropertyTypeFullName = property.ClrType.GetFirstGenericArgumentIfNullable().FullName, - TenantId = GetTenantId(entityEntry.Entity) + PropertyTypeFullName = property.ClrType.GetFirstGenericArgumentIfNullable().FullName }); } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs index 38f32180cb..752ccb953c 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs @@ -78,11 +78,33 @@ namespace Volo.Abp.AuditLogging HttpStatusCode = auditInfo.HttpStatusCode; ImpersonatorUserId = auditInfo.ImpersonatorUserId; ImpersonatorTenantId = auditInfo.ImpersonatorTenantId; - ExtraProperties = auditInfo.ExtraProperties.ToDictionary(pair => pair.Key, pair => pair.Value); - EntityChanges = auditInfo.EntityChanges.Select(e => new EntityChange(guidGenerator, Id, e)).ToList(); - Actions = auditInfo.Actions.Select(e => new AuditLogAction(guidGenerator.Create(), Id, e)).ToList(); - Exceptions = auditInfo.Exceptions.JoinAsString(Environment.NewLine).Truncate(AuditLogConsts.MaxExceptionsLength); - Comments = auditInfo.Comments.JoinAsString(Environment.NewLine).Truncate(AuditLogConsts.MaxCommentsLength); + + ExtraProperties = auditInfo + .ExtraProperties? + .ToDictionary(pair => pair.Key, pair => pair.Value) + ?? new Dictionary(); + + EntityChanges = auditInfo + .EntityChanges? + .Select(entityChangeInfo => new EntityChange(guidGenerator, Id, entityChangeInfo, tenantId: auditInfo.TenantId)) + .ToList() + ?? new List(); + + Actions = auditInfo + .Actions? + .Select(auditLogActionInfo => new AuditLogAction(guidGenerator.Create(), Id, auditLogActionInfo, tenantId: auditInfo.TenantId)) + .ToList() + ?? new List(); + + Exceptions = auditInfo + .Exceptions? + .JoinAsString(Environment.NewLine) + .Truncate(AuditLogConsts.MaxExceptionsLength); + + Comments = auditInfo + .Comments? + .JoinAsString(Environment.NewLine) + .Truncate(AuditLogConsts.MaxCommentsLength); } } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLogAction.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLogAction.cs index 27291eca9b..b6253e3f0e 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLogAction.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLogAction.cs @@ -32,11 +32,11 @@ namespace Volo.Abp.AuditLogging ExtraProperties = new Dictionary(); } - public AuditLogAction(Guid id, Guid auditLogId, AuditLogActionInfo actionInfo) + public AuditLogAction(Guid id, Guid auditLogId, AuditLogActionInfo actionInfo, Guid? tenantId = null) { Id = id; - TenantId = actionInfo.TenantId; + TenantId = tenantId; AuditLogId = auditLogId; ExecutionTime = actionInfo.ExecutionTime; ExecutionDuration = actionInfo.ExecutionDuration; diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChange.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChange.cs index 5a9d263d30..54ee88e11c 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChange.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChange.cs @@ -20,6 +20,8 @@ namespace Volo.Abp.AuditLogging public virtual EntityChangeType ChangeType { get; protected set; } + public virtual Guid? EntityTenantId { get; protected set; } + public virtual string EntityId { get; protected set; } public virtual string EntityTypeFullName { get; protected set; } @@ -33,17 +35,30 @@ namespace Volo.Abp.AuditLogging ExtraProperties = new Dictionary(); } - public EntityChange(IGuidGenerator guidGenerator, Guid auditLogId, EntityChangeInfo entityChangeInfo) + public EntityChange( + IGuidGenerator guidGenerator, + Guid auditLogId, + EntityChangeInfo entityChangeInfo, + Guid? tenantId = null) { Id = guidGenerator.Create(); AuditLogId = auditLogId; - TenantId = entityChangeInfo.TenantId; + TenantId = tenantId; ChangeTime = entityChangeInfo.ChangeTime; ChangeType = entityChangeInfo.ChangeType; EntityId = entityChangeInfo.EntityId.Truncate(EntityChangeConsts.MaxEntityTypeFullNameLength); EntityTypeFullName = entityChangeInfo.EntityTypeFullName.TruncateFromBeginning(EntityChangeConsts.MaxEntityTypeFullNameLength); - PropertyChanges = entityChangeInfo.PropertyChanges.Select(p => new EntityPropertyChange(guidGenerator, Id, p)).ToList(); - ExtraProperties = entityChangeInfo.ExtraProperties.ToDictionary(pair => pair.Key, pair => pair.Value); + + PropertyChanges = entityChangeInfo + .PropertyChanges? + .Select(p => new EntityPropertyChange(guidGenerator, Id, p, tenantId)) + .ToList() + ?? new List(); + + ExtraProperties = entityChangeInfo + .ExtraProperties? + .ToDictionary(pair => pair.Key, pair => pair.Value) + ?? new Dictionary(); } } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityPropertyChange.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityPropertyChange.cs index 7cd11797a5..2d60270e20 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityPropertyChange.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityPropertyChange.cs @@ -26,10 +26,14 @@ namespace Volo.Abp.AuditLogging } - public EntityPropertyChange(IGuidGenerator guidGenerator, Guid entityChangeId, EntityPropertyChangeInfo entityChangeInfo) + public EntityPropertyChange( + IGuidGenerator guidGenerator, + Guid entityChangeId, + EntityPropertyChangeInfo entityChangeInfo, + Guid? tenantId = null) { Id = guidGenerator.Create(); - TenantId = entityChangeInfo.TenantId; + TenantId = tenantId; EntityChangeId = entityChangeId; NewValue = entityChangeInfo.NewValue.Truncate(EntityPropertyChangeConsts.MaxNewValueLength); OriginalValue = entityChangeInfo.OriginalValue.Truncate(EntityPropertyChangeConsts.MaxOriginalValueLength); diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditStore_Basic_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditStore_Basic_Tests.cs index b21df0b025..c54d98608c 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditStore_Basic_Tests.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditStore_Basic_Tests.cs @@ -78,7 +78,6 @@ namespace Volo.Abp.AuditLogging insertedLog.EntityChanges.First().PropertyChanges.Count.ShouldBeGreaterThan(0); } - [Fact] public async Task Should_Get_List_Of_Audit_Logs() { diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.Tests/Volo/Abp/AuditLogging/MultiTenantAuditLog_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.Tests/Volo/Abp/AuditLogging/MultiTenantAuditLog_Tests.cs new file mode 100644 index 0000000000..6e92bc6f7f --- /dev/null +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.Tests/Volo/Abp/AuditLogging/MultiTenantAuditLog_Tests.cs @@ -0,0 +1,76 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Auditing; +using Xunit; + +namespace Volo.Abp.AuditLogging +{ + public class MultiTenantAuditLog_Tests : AuditLogsTestBase + { + private readonly IAuditingManager _auditingManager; + private readonly IAuditLogRepository _auditLogRepository; + + public MultiTenantAuditLog_Tests() + { + _auditingManager = GetRequiredService(); + _auditLogRepository = GetRequiredService(); + } + + [Fact] + public async Task Should_Save_Audit_Logs_To_The_Tenant_Begins_The_Scope() + { + //Arrange + + var applicationName = Guid.NewGuid().ToString(); + var tenantId = Guid.NewGuid(); + var entityId1 = Guid.NewGuid(); + var entityId2 = Guid.NewGuid(); + + //Act + + using (var scope = _auditingManager.BeginScope()) + { + _auditingManager.Current.Log.ApplicationName = applicationName; + + //Creating a host entity + _auditingManager.Current.Log.EntityChanges.Add( + new EntityChangeInfo + { + ChangeTime = DateTime.Now, + ChangeType = EntityChangeType.Created, + EntityEntry = new object(), + EntityId = entityId1.ToString(), + EntityTypeFullName = "TestEntity" + } + ); + + //Creating a tenant entity + _auditingManager.Current.Log.EntityChanges.Add( + new EntityChangeInfo + { + ChangeTime = DateTime.Now, + ChangeType = EntityChangeType.Created, + EntityEntry = new object(), + EntityId = entityId2.ToString(), + EntityTypeFullName = "TestEntity", + EntityTenantId = tenantId + } + ); + + await scope.SaveAsync(); + } + + //Assert + + var auditLogs = await _auditLogRepository.GetListAsync(applicationName: applicationName, includeDetails: true); + auditLogs.Count.ShouldBe(1); + var auditLog = auditLogs.First(); + auditLog.EntityChanges.ShouldNotBeNull(); + auditLog.EntityChanges.Count.ShouldBe(2); + auditLog.EntityChanges.ShouldContain(e => e.EntityId == entityId1.ToString()); + auditLog.EntityChanges.ShouldContain(e => e.EntityId == entityId2.ToString()); + } + } +} From a391e05008dfba4a020e7129f9e6cb96fdce1d1a Mon Sep 17 00:00:00 2001 From: Mostafa F Date: Mon, 1 Apr 2019 20:15:47 +0430 Subject: [PATCH 016/116] fixed #945 --- .../Themes/Basic/Components/Menu/_MenuItem.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml index fb8feb2ad8..4e2dd80fbf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml @@ -7,7 +7,7 @@ } @if (Model.IsLeaf) { - @if (Model.Url != null) + if (Model.Url != null) { @Model.DisplayName From 94bc5fc06f63a2d0b330e79f953d971175c31558 Mon Sep 17 00:00:00 2001 From: hitaspdotnet Date: Mon, 1 Apr 2019 20:22:48 +0430 Subject: [PATCH 017/116] fixed #945 --- .../Themes/Basic/Components/Menu/Default.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml index 7bfe8f1730..cace550248 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml @@ -7,7 +7,7 @@ var disabled = menuItem.IsDisabled ? "disabled" : string.Empty; if (menuItem.IsLeaf) { - @if (menuItem.Url != null) + if (menuItem.Url != null) { /// Mail to be normalized - protected virtual void NormalizeMail(MailMessage mail) + protected virtual async Task NormalizeMailAsync(MailMessage mail) { if (mail.From == null || mail.From.Address.IsNullOrEmpty()) { mail.From = new MailAddress( - Configuration.DefaultFromAddress, - Configuration.DefaultFromDisplayName, + await Configuration.GetDefaultFromAddressAsync(), + await Configuration.GetDefaultFromDisplayNameAsync(), Encoding.UTF8 ); } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs index 3034481471..e02b78badd 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs @@ -1,19 +1,16 @@ using System; +using System.Threading.Tasks; using Volo.Abp.Settings; namespace Volo.Abp.Emailing { /// - /// Implementation of that reads settings - /// from . + /// Base implementation of that reads settings + /// from . /// public abstract class EmailSenderConfiguration : IEmailSenderConfiguration { - public virtual string DefaultFromAddress => GetNotEmptySettingValue(EmailSettingNames.DefaultFromAddress); - - public virtual string DefaultFromDisplayName => SettingProvider.GetOrNull(EmailSettingNames.DefaultFromDisplayName); - - protected readonly ISettingProvider SettingProvider; + protected ISettingProvider SettingProvider { get; } /// /// Creates a new . @@ -23,14 +20,24 @@ namespace Volo.Abp.Emailing SettingProvider = settingProvider; } + public Task GetDefaultFromAddressAsync() + { + return GetNotEmptySettingValueAsync(EmailSettingNames.DefaultFromAddress); + } + + public Task GetDefaultFromDisplayNameAsync() + { + return GetNotEmptySettingValueAsync(EmailSettingNames.DefaultFromDisplayName); + } + /// /// Gets a setting value by checking. Throws if it's null or empty. /// /// Name of the setting /// Value of the setting - protected string GetNotEmptySettingValue(string name) + protected async Task GetNotEmptySettingValueAsync(string name) { - var value = SettingProvider.GetOrNull(name); + var value = await SettingProvider.GetOrNullAsync(name); if (value.IsNullOrEmpty()) { diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs index 382b2eb6cc..32d1fb8c1c 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs @@ -18,8 +18,8 @@ namespace Volo.Abp.Emailing new SettingDefinition(EmailSettingNames.Smtp.Domain), new SettingDefinition(EmailSettingNames.Smtp.EnableSsl, "false"), new SettingDefinition(EmailSettingNames.Smtp.UseDefaultCredentials, "true"), - new SettingDefinition(EmailSettingNames.DefaultFromAddress), - new SettingDefinition(EmailSettingNames.DefaultFromDisplayName) + new SettingDefinition(EmailSettingNames.DefaultFromAddress, "noreply@abp.io"), + new SettingDefinition(EmailSettingNames.DefaultFromDisplayName, "ABP application") ); } } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs index fc997af1ce..ffbc556ea5 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs @@ -1,18 +1,14 @@ -namespace Volo.Abp.Emailing +using System.Threading.Tasks; + +namespace Volo.Abp.Emailing { /// /// Defines configurations used while sending emails. /// public interface IEmailSenderConfiguration { - /// - /// Default from address. - /// - string DefaultFromAddress { get; } - - /// - /// Default display name. - /// - string DefaultFromDisplayName { get; } + Task GetDefaultFromAddressAsync(); + + Task GetDefaultFromDisplayNameAsync(); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs index 989ae6e372..4af7e25b06 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs @@ -1,4 +1,5 @@ using System.Net.Mail; +using System.Threading.Tasks; namespace Volo.Abp.Emailing.Smtp { @@ -13,6 +14,6 @@ namespace Volo.Abp.Emailing.Smtp /// /// An object that is ready to send emails. /// - SmtpClient BuildClient(); + Task BuildClientAsync(); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs index b6abce9249..853fcc2d8e 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs @@ -1,4 +1,6 @@ -namespace Volo.Abp.Emailing.Smtp +using System.Threading.Tasks; + +namespace Volo.Abp.Emailing.Smtp { /// /// Defines configurations to used by SmtpClient object. @@ -8,36 +10,36 @@ /// /// SMTP Host name/IP. /// - string Host { get; } + Task GetHostAsync(); /// /// SMTP Port. /// - int Port { get; } + Task GetPortAsync(); /// /// User name to login to SMTP server. /// - string UserName { get; } + Task GetUserNameAsync(); /// /// Password to login to SMTP server. /// - string Password { get; } + Task GetPasswordAsync(); /// /// Domain name to login to SMTP server. /// - string Domain { get; } + Task GetDomainAsync(); /// /// Is SSL enabled? /// - bool EnableSsl { get; } + Task GetEnableSslAsync(); /// /// Use default credentials? /// - bool UseDefaultCredentials { get; } + Task GetUseDefaultCredentialsAsync(); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs index 966c1b3dbb..f7f431eaca 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs @@ -4,6 +4,7 @@ using System.Net.Mail; using System.Threading.Tasks; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; namespace Volo.Abp.Emailing.Smtp { @@ -12,31 +13,34 @@ namespace Volo.Abp.Emailing.Smtp /// public class SmtpEmailSender : EmailSenderBase, ISmtpEmailSender, ITransientDependency { - private readonly ISmtpEmailSenderConfiguration _configuration; + protected ISmtpEmailSenderConfiguration SmtpConfiguration { get; } /// /// Creates a new . /// - public SmtpEmailSender(ISmtpEmailSenderConfiguration configuration, IBackgroundJobManager backgroundJobManager) - : base(configuration, backgroundJobManager) + public SmtpEmailSender( + ISmtpEmailSenderConfiguration smtpConfiguration, + IBackgroundJobManager backgroundJobManager) + : base(smtpConfiguration, backgroundJobManager) { - _configuration = configuration; + SmtpConfiguration = smtpConfiguration; } - public SmtpClient BuildClient() + public async Task BuildClientAsync() { - var host = _configuration.Host; - var port = _configuration.Port; + var host = await SmtpConfiguration.GetHostAsync(); + var port = await SmtpConfiguration.GetPortAsync(); var smtpClient = new SmtpClient(host, port); + try { - if (_configuration.EnableSsl) + if (await SmtpConfiguration.GetEnableSslAsync()) { smtpClient.EnableSsl = true; } - if (_configuration.UseDefaultCredentials) + if (await SmtpConfiguration.GetUseDefaultCredentialsAsync()) { smtpClient.UseDefaultCredentials = true; } @@ -44,11 +48,11 @@ namespace Volo.Abp.Emailing.Smtp { smtpClient.UseDefaultCredentials = false; - var userName = _configuration.UserName; + var userName = await SmtpConfiguration.GetUserNameAsync(); if (!userName.IsNullOrEmpty()) { - var password = _configuration.Password; - var domain = _configuration.Domain; + var password = await SmtpConfiguration.GetPasswordAsync(); + var domain = await SmtpConfiguration.GetDomainAsync(); smtpClient.Credentials = !domain.IsNullOrEmpty() ? new NetworkCredential(userName, password, domain) : new NetworkCredential(userName, password); @@ -66,7 +70,7 @@ namespace Volo.Abp.Emailing.Smtp protected override async Task SendEmailAsync(MailMessage mail) { - using (var smtpClient = BuildClient()) + using (var smtpClient = await BuildClientAsync()) { await smtpClient.SendMailAsync(mail); } @@ -74,7 +78,7 @@ namespace Volo.Abp.Emailing.Smtp protected override void SendEmail(MailMessage mail) { - using (var smtpClient = BuildClient()) + using (var smtpClient = AsyncHelper.RunSync(BuildClientAsync)) { smtpClient.Send(mail); } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs index 678d2cc4f2..094ee8dd97 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Settings; @@ -6,28 +7,49 @@ namespace Volo.Abp.Emailing.Smtp { /// /// Implementation of that reads settings - /// from . + /// from . /// public class SmtpEmailSenderConfiguration : EmailSenderConfiguration, ISmtpEmailSenderConfiguration, ITransientDependency { - public virtual string Host => GetNotEmptySettingValue(EmailSettingNames.Smtp.Host); + public SmtpEmailSenderConfiguration(ISettingProvider settingProvider) + : base(settingProvider) + { - public virtual int Port => GetNotEmptySettingValue(EmailSettingNames.Smtp.Port).To(); + } - public virtual string UserName => GetNotEmptySettingValue(EmailSettingNames.Smtp.UserName); + public Task GetHostAsync() + { + return GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.Host); + } - public virtual string Password => GetNotEmptySettingValue(EmailSettingNames.Smtp.Password); + public async Task GetPortAsync() + { + return (await GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.Port)).To(); + } - public virtual string Domain => SettingProvider.GetOrNull(EmailSettingNames.Smtp.Domain); + public Task GetUserNameAsync() + { + return GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.UserName); + } - public virtual bool EnableSsl => SettingProvider.GetOrNull(EmailSettingNames.Smtp.EnableSsl).To(); + public Task GetPasswordAsync() + { + return GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.Password); + } - public virtual bool UseDefaultCredentials => SettingProvider.GetOrNull(EmailSettingNames.Smtp.UseDefaultCredentials).To(); + public Task GetDomainAsync() + { + return SettingProvider.GetOrNullAsync(EmailSettingNames.Smtp.Domain); + } - public SmtpEmailSenderConfiguration(ISettingProvider settingProvider) - : base(settingProvider) + public async Task GetEnableSslAsync() { + return (await GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.EnableSsl)).To(); + } + public async Task GetUseDefaultCredentialsAsync() + { + return (await GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.UseDefaultCredentials)).To(); } } } \ No newline at end of file From bb967c72a95a378d7ef75d888629cc24abe48a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20=C3=96zceviz?= Date: Tue, 2 Apr 2019 12:50:29 +0200 Subject: [PATCH 019/116] Create Dockerfile --- .../service/host/IdentityServerHost/Dockerfile | 18 ++++++++++++++++++ .../Dockerfile | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 templates/service/host/IdentityServerHost/Dockerfile create mode 100644 templates/service/host/MyCompanyName.MyProjectName.Host/Dockerfile diff --git a/templates/service/host/IdentityServerHost/Dockerfile b/templates/service/host/IdentityServerHost/Dockerfile new file mode 100644 index 0000000000..38f985b7f3 --- /dev/null +++ b/templates/service/host/IdentityServerHost/Dockerfile @@ -0,0 +1,18 @@ +FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +WORKDIR /app +EXPOSE 80 + +FROM microsoft/dotnet:2.2-sdk AS build +WORKDIR /src +COPY . . +WORKDIR /src/templates/service/host/IdentityServerHost +RUN dotnet restore -nowarn:msb3202,nu1503 +RUN dotnet build --no-restore -c Release -o /app + +FROM build AS publish +RUN dotnet publish --no-restore -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "IdentityServerHost.dll"] diff --git a/templates/service/host/MyCompanyName.MyProjectName.Host/Dockerfile b/templates/service/host/MyCompanyName.MyProjectName.Host/Dockerfile new file mode 100644 index 0000000000..5af6a5712c --- /dev/null +++ b/templates/service/host/MyCompanyName.MyProjectName.Host/Dockerfile @@ -0,0 +1,18 @@ +FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +WORKDIR /app +EXPOSE 80 + +FROM microsoft/dotnet:2.2-sdk AS build +WORKDIR /src +COPY . . +WORKDIR /src/templates/service/host/MyCompanyName.MyProjectName.Host +RUN dotnet restore -nowarn:msb3202,nu1503 +RUN dotnet build --no-restore -c Release -o /app + +FROM build AS publish +RUN dotnet publish --no-restore -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "MyCompanyName.MyProjectName.Host.dll"] From 79c17ef7e55aa0b63e13226309912a1d1b6327df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20=C3=96zceviz?= Date: Tue, 2 Apr 2019 12:52:21 +0200 Subject: [PATCH 020/116] Create Dockerfile for migrations --- templates/service/database/Dockerfile | 21 +++++++++++++++++++++ templates/service/database/entrypoint.sh | 12 ++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 templates/service/database/Dockerfile create mode 100644 templates/service/database/entrypoint.sh diff --git a/templates/service/database/Dockerfile b/templates/service/database/Dockerfile new file mode 100644 index 0000000000..74bbe2c820 --- /dev/null +++ b/templates/service/database/Dockerfile @@ -0,0 +1,21 @@ +FROM microsoft/dotnet:2.2-sdk-alpine AS build +RUN apk add --no-cache bash +COPY . . + +WORKDIR /templates/service/host/IdentityServerHost +RUN dotnet restore -nowarn:msb3202,nu1503 +RUN dotnet build --no-restore -c Release + +WORKDIR /templates/service/host/MyCompanyName.MyProjectName.Host +RUN dotnet restore -nowarn:msb3202,nu1503 +RUN dotnet build --no-restore -c Release + +FROM build AS final +WORKDIR /src +COPY --from=build /templates/service/host/IdentityServerHost ./IdentityServerHost +COPY --from=build /templates/service/host/MyCompanyName.MyProjectName.Host ./MyCompanyName.MyProjectName.Host +COPY --from=build /templates/service/database/entrypoint.sh . +RUN /bin/bash -c "sed -i $'s/\r$//' entrypoint.sh" +RUN chmod +x ./entrypoint.sh + +ENTRYPOINT ["./entrypoint.sh"] \ No newline at end of file diff --git a/templates/service/database/entrypoint.sh b/templates/service/database/entrypoint.sh new file mode 100644 index 0000000000..80f1b43dbc --- /dev/null +++ b/templates/service/database/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +cd IdentityServerHost +export ConnectionStrings__Default=$IdentityServerConnectionString + +until dotnet ef database update --no-build; do +>&2 echo "SQL Server is starting up" +sleep 1 +done + +export ConnectionStrings__Default=$MyProjectNameConnectionString +cd MyCompanyName.MyProjectName.Host && dotnet ef database update --no-build \ No newline at end of file From 56e49c30c34ff9253922c8001986ca9e040d6f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20=C3=96zceviz?= Date: Tue, 2 Apr 2019 12:53:03 +0200 Subject: [PATCH 021/116] Create docker-compose yml files --- .../service/docker-compose.migrations.yml | 12 +++++++++ templates/service/docker-compose.override.yml | 26 +++++++++++++++++++ templates/service/docker-compose.yml | 25 ++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 templates/service/docker-compose.migrations.yml create mode 100644 templates/service/docker-compose.override.yml create mode 100644 templates/service/docker-compose.yml diff --git a/templates/service/docker-compose.migrations.yml b/templates/service/docker-compose.migrations.yml new file mode 100644 index 0000000000..33d32f8faf --- /dev/null +++ b/templates/service/docker-compose.migrations.yml @@ -0,0 +1,12 @@ +version: '3.4' + +services: + migrations: + build: + context: ../../ + dockerfile: templates/service/database/Dockerfile + depends_on: + - sqlserver + environment: + - IdentityServerConnectionString=Server=sqlserver;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + - MyProjectNameConnectionString=Server=sqlserver;Database=MyProjectName_ModuleDb;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false diff --git a/templates/service/docker-compose.override.yml b/templates/service/docker-compose.override.yml new file mode 100644 index 0000000000..6fb4c52fa2 --- /dev/null +++ b/templates/service/docker-compose.override.yml @@ -0,0 +1,26 @@ +version: '3.4' + +services: + sqlserver: + environment: + - SA_PASSWORD=yourStrong(!)Password + - ACCEPT_EULA=Y + ports: + - "1433:1433" + + identity-server: + environment: + - ConnectionStrings__Default=Server=sqlserver;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + - ConnectionStrings__SqlServerCache=Server=sqlserver;Database=MyProjectName_Cache;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + ports: + - "51600:80" + + my-project-name: + environment: + - ConnectionStrings__Default=Server=sqlserver;Database=MyProjectName_ModuleDb;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + - ConnectionStrings__AbpSettingManagement=Server=sqlserver;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + - ConnectionStrings__AbpPermissionManagement=Server=sqlserver;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + - ConnectionStrings__AbpAuditLogging=Server=sqlserver;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + - ConnectionStrings__SqlServerCache=Server=sqlserver;Database=MyProjectName_Cache;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + ports: + - "51601:80" \ No newline at end of file diff --git a/templates/service/docker-compose.yml b/templates/service/docker-compose.yml new file mode 100644 index 0000000000..0e89061477 --- /dev/null +++ b/templates/service/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3.4' + +services: + sqlserver: + image: mcr.microsoft.com/mssql/server + volumes: + - dbdata:/var/opt/mssql + + identity-server: + build: + context: ../../ + dockerfile: templates/service/host/IdentityServerHost/Dockerfile + depends_on: + - sqlserver + + my-project-name: + build: + context: ../../ + dockerfile: templates/service/host/MyCompanyName.MyProjectName.Host/Dockerfile + depends_on: + - sqlserver + - identity-server + +volumes: + dbdata: \ No newline at end of file From 9a9420cb51328e5351d384bef0579a5055ac343d Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 2 Apr 2019 15:04:50 +0300 Subject: [PATCH 022/116] Make IAppUrlProvider methods async --- .../Abp/Ui/Navigation/Urls/AppUrlOptions.cs | 4 +- .../Abp/Ui/Navigation/Urls/AppUrlProvider.cs | 80 +++++++++++++++++-- .../Abp/Ui/Navigation/Urls/IAppUrlProvider.cs | 5 +- 3 files changed, 78 insertions(+), 11 deletions(-) diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs index e46bac2c66..1c6065adea 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs @@ -1,6 +1,4 @@ -using Volo.Abp.Ui.Navigation.Urls; - -namespace Volo.Abp.Ui.Navigation.Urls +namespace Volo.Abp.Ui.Navigation.Urls { public class AppUrlOptions { diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs index f55b741579..e6a5747ae2 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs @@ -1,20 +1,42 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Ui.Navigation.Urls { public class AppUrlProvider : IAppUrlProvider, ITransientDependency { + public const string TenantIdPlaceHolder = "{{tenantId}}"; + public const string TenantNamePlaceHolder = "{{tenantName}}"; + protected AppUrlOptions Options { get; } + protected ICurrentTenant CurrentTenant { get; } + protected ITenantStore TenantStore { get; } - public AppUrlProvider(IOptions options) + public AppUrlProvider( + IOptions options, + ICurrentTenant currentTenant, + ITenantStore tenantStore) { + CurrentTenant = currentTenant; + TenantStore = tenantStore; Options = options.Value; } - public virtual string GetUrl(string appName, string urlName = null) + public virtual async Task GetUrlAsync(string appName, string urlName = null) + { + return await ReplacePlaceHoldersAsync( + await GetConfiguredUrl( + appName, + urlName + ) + ); + } + + protected virtual Task GetConfiguredUrl(string appName, string urlName) { var app = Options.Applications[appName]; @@ -22,24 +44,70 @@ namespace Volo.Abp.Ui.Navigation.Urls { if (app.RootUrl.IsNullOrEmpty()) { - throw new AbpException($"RootUrl for the application '{appName}' was not configured. Use {nameof(AppUrlOptions)} to configure it!"); + throw new AbpException( + $"RootUrl for the application '{appName}' was not configured. Use {nameof(AppUrlOptions)} to configure it!" + ); } - return app.RootUrl; + return Task.FromResult(app.RootUrl); } var url = app.Urls.GetOrDefault(urlName); if (url.IsNullOrEmpty()) { - throw new AbpException($"Url, named '{urlName}', for the application '{appName}' was not configured. Use {nameof(AppUrlOptions)} to configure it!"); + throw new AbpException( + $"Url, named '{urlName}', for the application '{appName}' was not configured. Use {nameof(AppUrlOptions)} to configure it!" + ); } if (app.RootUrl == null) + { + return Task.FromResult(url); + } + + return Task.FromResult(app.RootUrl.EnsureEndsWith('/') + url); + } + + protected virtual async Task ReplacePlaceHoldersAsync(string url) + { + url = url.Replace( + TenantIdPlaceHolder, + CurrentTenant.Id.HasValue ? CurrentTenant.Id.Value.ToString() : "" + ); + + if (!url.Contains(TenantNamePlaceHolder)) { return url; } - return app.RootUrl.EnsureEndsWith('/') + url; + var tenantNamePlaceHolder = TenantNamePlaceHolder; + + if (url.Contains(TenantNamePlaceHolder + '.')) + { + tenantNamePlaceHolder = TenantNamePlaceHolder + '.'; + } + + if (CurrentTenant.Id.HasValue) + { + url = url.Replace(tenantNamePlaceHolder, await GetCurrentTenantNameAsync()); + } + else + { + url = url.Replace(tenantNamePlaceHolder, ""); + } + + return url; + } + + private async Task GetCurrentTenantNameAsync() + { + if (CurrentTenant.Id.HasValue && CurrentTenant.Name.IsNullOrEmpty()) + { + var tenantConfiguration = await TenantStore.FindAsync(CurrentTenant.Id.Value); + return tenantConfiguration.Name; + } + + return CurrentTenant.Name; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs index ac7ce1abd5..325117a24e 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs @@ -1,9 +1,10 @@ -using JetBrains.Annotations; +using System.Threading.Tasks; +using JetBrains.Annotations; namespace Volo.Abp.Ui.Navigation.Urls { public interface IAppUrlProvider { - string GetUrl([NotNull] string appName, [CanBeNull] string urlName = null); + Task GetUrlAsync([NotNull] string appName, [CanBeNull] string urlName = null); } } From 78b8422701818a8b79999d4e5b4aa3a7edad84f3 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 2 Apr 2019 15:05:10 +0300 Subject: [PATCH 023/116] Create NullTenantResolveResultAccessor and move ITenantResolveResultAccessor to Volo.Abp.MultiTenancy package --- .../HttpContextTenantResolveResultAccessor.cs | 5 ++--- .../MultiTenancy/ITenantResolveResultAccessor.cs | 5 +++-- .../MultiTenancy/NullTenantResolveResultAccessor.cs | 13 +++++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) rename framework/src/{Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore => Volo.Abp.MultiTenancy/Volo/Abp}/MultiTenancy/ITenantResolveResultAccessor.cs (58%) create mode 100644 framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantResolveResultAccessor.cs diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs index 4865489248..6c44195b1a 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs @@ -1,13 +1,12 @@ -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; namespace Volo.Abp.AspNetCore.MultiTenancy { + [Dependency(ReplaceServices = true)] public class HttpContextTenantResolveResultAccessor : ITenantResolveResultAccessor, ITransientDependency { - [CanBeNull] public TenantResolveResult Result { get diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/ITenantResolveResultAccessor.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveResultAccessor.cs similarity index 58% rename from framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/ITenantResolveResultAccessor.cs rename to framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveResultAccessor.cs index 90201dcf2d..f8acb22538 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/ITenantResolveResultAccessor.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveResultAccessor.cs @@ -1,9 +1,10 @@ -using Volo.Abp.MultiTenancy; +using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.MultiTenancy { public interface ITenantResolveResultAccessor { + [CanBeNull] TenantResolveResult Result { get; set; } } } diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantResolveResultAccessor.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantResolveResultAccessor.cs new file mode 100644 index 0000000000..b97ab2c518 --- /dev/null +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantResolveResultAccessor.cs @@ -0,0 +1,13 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.MultiTenancy +{ + public class NullTenantResolveResultAccessor : ITenantResolveResultAccessor, ISingletonDependency + { + public TenantResolveResult Result + { + get => null; + set { } + } + } +} \ No newline at end of file From 18f09ebf130954e3ec6e1b79f9f70ab1c395c65b Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 2 Apr 2019 15:05:50 +0300 Subject: [PATCH 024/116] resolved #938 --- .../Steps/NugetReferenceReplaceStep.cs | 39 ++----------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/NugetReferenceReplaceStep.cs b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/NugetReferenceReplaceStep.cs index 25cdbc988e..b32d747fc0 100644 --- a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/NugetReferenceReplaceStep.cs +++ b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/NugetReferenceReplaceStep.cs @@ -16,7 +16,8 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps new NugetReferenceReplacer( context.Files, "MyCompanyName", - "MyProjectName" + "MyProjectName", + context.Template.Version ).Run(); } @@ -27,12 +28,12 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps private readonly string _projectNamePlaceHolder; private readonly string _latestNugetPackageVersion; - public NugetReferenceReplacer(List entries, string companyNamePlaceHolder, string projectNamePlaceHolder) + public NugetReferenceReplacer(List entries, string companyNamePlaceHolder, string projectNamePlaceHolder, string latestNugetPackageVersion) { _entries = entries; _companyNamePlaceHolder = companyNamePlaceHolder; _projectNamePlaceHolder = projectNamePlaceHolder; - _latestNugetPackageVersion = GetLatestNugetPackageVersion(); + _latestNugetPackageVersion = latestNugetPackageVersion; } public void Run() @@ -110,38 +111,6 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps stream.Position = 0; return stream; } - - private string GetLatestNugetPackageVersion() - { - //TODO: This should get it from the related release! Not always from the master! - - var commonPropsUrl = "https://raw.githubusercontent.com/abpframework/abp/master/common.props"; - var content = ""; - using (var webClient = new WebClient()) - { - try - { - content = webClient.DownloadString(commonPropsUrl); - } - catch (Exception) - { - throw new Exception("The Common.pros doesn't exist on github or removed to anywhere else."); - } - } - - var doc = new HtmlDocument(); - - doc.Load(GenerateStreamFromString(content)); - - try - { - return doc.DocumentNode.SelectNodes("//version").FirstOrDefault().InnerHtml.Trim(); - } - catch (Exception e) - { - return ""; - } - } } } } From 7a7fd33efbf5a4416069500ae079d481ca87f2fe Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Tue, 2 Apr 2019 15:49:49 +0300 Subject: [PATCH 025/116] closes abpframework/abp#942 --- .../Controllers/ErrorController.cs | 127 ++++++++++++++++++ .../Controllers/HomeController.cs | 3 +- .../app/Volo.DocsTestApp/DocsTestAppModule.cs | 5 +- .../app/Volo.DocsTestApp/Pages/Index.cshtml | 4 +- .../docs/src/Volo.Docs.Web/DocsWebModule.cs | 2 + .../Pages/Documents/Project/Index.cshtml.cs | 10 +- 6 files changed, 143 insertions(+), 8 deletions(-) create mode 100644 modules/docs/app/Volo.DocsTestApp/Controllers/ErrorController.cs diff --git a/modules/docs/app/Volo.DocsTestApp/Controllers/ErrorController.cs b/modules/docs/app/Volo.DocsTestApp/Controllers/ErrorController.cs new file mode 100644 index 0000000000..943a3a85f2 --- /dev/null +++ b/modules/docs/app/Volo.DocsTestApp/Controllers/ErrorController.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Net; +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using Volo.Abp.AspNetCore.Mvc; + +namespace Volo.DocsTestApp.Controllers +{ + public class ErrorController : AbpController + { + [Route("error/{statusCode}")] + [HttpGet] + public IActionResult Index(int statusCode = 0) + { + var statusFeature = HttpContext.Features.Get(); + if (statusFeature != null) + { + Log.Warning("Handled {0} error for URL: {1}", statusCode, statusFeature.OriginalPath); + } + + var isValidStatusCode = Enum.IsDefined(typeof(HttpStatusCode), statusCode); + if (!isValidStatusCode) + { + statusCode = (int)HttpStatusCode.BadRequest; + } + + return new ContentResult + { + ContentType = System.Net.Mime.MediaTypeNames.Text.Html, + StatusCode = statusCode, + Content = string.Format(HtmlBody, _errorMessages.ContainsKey(statusCode) + ? _errorMessages[statusCode] + : "Looks like something went wrong!") + }; + } + + private const string HtmlBody = ""; + + /*For more ASCII arts http://patorjk.com/software/taag/#p=display&h=0&f=Big&t=400*/ + private readonly Dictionary _errorMessages = new Dictionary + { + { + 400, @" + _ _ ___ ___ + | || | / _ \ / _ \ + | || |_ | | | | | | | | + |__ _| | | | | | | | | + | | | |_| | | |_| | + |_| \___/ \___/ + +You've sent a bad request!" + }, + { + 401, @" + _ _ ___ __ + | || | / _ \ /_ | + | || |_ | | | | | | + |__ _| | | | | | | + | | | |_| | | | + |_| \___/ |_| + +Authorization required!" + }, + { + 403, + @" + _ _ ___ ____ + | || | / _ \ |___ \ + | || |_ | | | | __) | + |__ _| | | | | |__ < + | | | |_| | ___) | + |_| \___/ |____/ + +This is a forbidden area!" + }, + { + 404, @" + _ _ ___ _ _ + | || | / _ \ | || | + | || |_ | | | | | || |_ + |__ _| | | | | |__ _| + | | | |_| | | | + |_| \___/ |_| + +We can't find the page you're looking for..." + }, + { + 500, + @" + _____ ___ ___ + | ____| / _ \ / _ \ + | |__ | | | | | | | | + |___ \ | | | | | | | | + ___) | | |_| | | |_| | + |____/ \___/ \___/ + +Houston, we have a problem. Internal server error!" + }, + { + 502, + @" + _____ ___ ___ + | ____| / _ \ |__ \ + | |__ | | | | ) | + |___ \ | | | | / / + ___) | | |_| | / /_ + |____/ \___/ |____| + +Ooops! Our server is experiencing a mild case of the hiccups." + }, + { + 503, + @" + _____ ___ ____ + | ____| / _ \ |___ \ + | |__ | | | | __) | + |___ \ | | | | |__ < + ___) | | |_| | ___) | + |____/ \___/ |____/ + +Looks like we're having some server issues." + } + }; + } +} diff --git a/modules/docs/app/Volo.DocsTestApp/Controllers/HomeController.cs b/modules/docs/app/Volo.DocsTestApp/Controllers/HomeController.cs index a80cc2dfeb..0750e72b20 100644 --- a/modules/docs/app/Volo.DocsTestApp/Controllers/HomeController.cs +++ b/modules/docs/app/Volo.DocsTestApp/Controllers/HomeController.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Mvc; -using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; namespace Volo.DocsTestApp.Controllers { diff --git a/modules/docs/app/Volo.DocsTestApp/DocsTestAppModule.cs b/modules/docs/app/Volo.DocsTestApp/DocsTestAppModule.cs index 9e25b70345..275eb0f713 100644 --- a/modules/docs/app/Volo.DocsTestApp/DocsTestAppModule.cs +++ b/modules/docs/app/Volo.DocsTestApp/DocsTestAppModule.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using Microsoft.AspNetCore.Builder; @@ -108,7 +109,7 @@ namespace Volo.DocsTestApp app.UseDeveloperExceptionPage(); app.UseVirtualFiles(); - + app.UseSwagger(); app.UseSwaggerUI(options => { @@ -118,6 +119,8 @@ namespace Volo.DocsTestApp app.UseAuthentication(); app.UseRequestLocalization(app.ApplicationServices.GetRequiredService>().Value); + + app.UseStatusCodePagesWithReExecute("/error/{0}"); app.UseMvc(routes => { diff --git a/modules/docs/app/Volo.DocsTestApp/Pages/Index.cshtml b/modules/docs/app/Volo.DocsTestApp/Pages/Index.cshtml index f47edcbd98..ca46c1b442 100644 --- a/modules/docs/app/Volo.DocsTestApp/Pages/Index.cshtml +++ b/modules/docs/app/Volo.DocsTestApp/Pages/Index.cshtml @@ -1,4 +1,4 @@ @page @model Volo.DocsTestApp.Pages.IndexModel -

Welcome to the Docs demo application.

-Go to Docs \ No newline at end of file +

Welcome to the Docs Demo!

+Go to documents... \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/DocsWebModule.cs b/modules/docs/src/Volo.Docs.Web/DocsWebModule.cs index 689b9ec929..89301265ea 100644 --- a/modules/docs/src/Volo.Docs.Web/DocsWebModule.cs +++ b/modules/docs/src/Volo.Docs.Web/DocsWebModule.cs @@ -38,6 +38,8 @@ namespace Volo.Docs Configure(options => { //TODO: Make configurable! + options.Conventions.AddPageRoute("/Documents/Project/Index", "documents/{projectName}"); + options.Conventions.AddPageRoute("/Documents/Project/Index", "documents/{projectName}/{version}/{*documentName}"); }); diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs index a80aeb474c..378903d121 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs @@ -42,8 +42,8 @@ namespace Volo.Docs.Pages.Documents.Project private readonly IProjectAppService _projectAppService; public IndexModel( - IDocumentAppService documentAppService, - IDocumentToHtmlConverterFactory documentToHtmlConverterFactory, + IDocumentAppService documentAppService, + IDocumentToHtmlConverterFactory documentToHtmlConverterFactory, IProjectAppService projectAppService) { _documentAppService = documentAppService; @@ -80,6 +80,10 @@ namespace Volo.Docs.Pages.Documents.Project private async Task SetVersionAsync() { //TODO: Needs refactoring + if (string.IsNullOrWhiteSpace(Version)) + { + Version = DocsAppConsts.Latest; + } var output = await _projectAppService.GetVersionsAsync(Project.ShortName); var versions = output.Items @@ -142,7 +146,7 @@ namespace Volo.Docs.Pages.Documents.Project Navigation.ConvertItems(); } - + public string CreateVersionLink(VersionInfoViewModel latestVersion, string version, string documentName = null) { if (latestVersion == null || latestVersion.Version == version) From 6893ba762f0427c81c842a866a3e90191e287e42 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 2 Apr 2019 16:16:49 +0300 Subject: [PATCH 026/116] Resolved #950: Add tenant management module to the MVC startup template. --- ...mpanyName.MyProjectName.Application.csproj | 1 + .../MyProjectNameApplicationModule.cs | 8 +- .../MyCompanyName.MyProjectName.Domain.csproj | 1 + .../MyProjectNameDomainModule.cs | 10 ++- .../MyProjectNameMigrationsDbContext.cs | 2 + ....cs => 20190402131334_Initial.Designer.cs} | 77 ++++++++++++++++++- ...9_Initial.cs => 20190402131334_Initial.cs} | 54 +++++++++++++ ...ectNameMigrationsDbContextModelSnapshot.cs | 75 ++++++++++++++++++ ...me.EntityFrameworkCore.DbMigrations.csproj | 4 + .../MyProjectNameEntityFrameworkCoreModule.cs | 4 +- ...e.MyProjectName.EntityFrameworkCore.csproj | 1 + .../MongoDb/MyProjectNameMongoDbModule.cs | 4 +- ...MyCompanyName.MyProjectName.MongoDB.csproj | 1 + .../MyCompanyName.MyProjectName.Web.csproj | 1 + .../MyProjectNameWebModule.cs | 5 +- 15 files changed, 239 insertions(+), 9 deletions(-) rename templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/{20190320072839_Initial.Designer.cs => 20190402131334_Initial.Designer.cs} (89%) rename templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/{20190320072839_Initial.cs => 20190402131334_Initial.cs} (90%) diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj index f774c93818..56ba7e68ae 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj @@ -11,6 +11,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs index c23175b956..3373432a4c 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs @@ -1,16 +1,16 @@ -using MyCompanyName.MyProjectName.Permissions; -using Volo.Abp.Authorization.Permissions; -using Volo.Abp.AutoMapper; +using Volo.Abp.AutoMapper; using Volo.Abp.Identity; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement; +using Volo.Abp.TenantManagement; namespace MyCompanyName.MyProjectName { [DependsOn( typeof(MyProjectNameDomainModule), typeof(AbpIdentityApplicationModule), - typeof(AbpPermissionManagementApplicationModule) + typeof(AbpPermissionManagementApplicationModule), + typeof(AbpTenantManagementApplicationModule) )] public class MyProjectNameApplicationModule : AbpModule { diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj index 32d5d55698..0b829f04d8 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj @@ -12,6 +12,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs index 5fca840175..9717644f7a 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs @@ -6,7 +6,9 @@ using Volo.Abp.Identity; using Volo.Abp.Localization; using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; using Volo.Abp.PermissionManagement.Identity; +using Volo.Abp.TenantManagement; using Volo.Abp.VirtualFileSystem; namespace MyCompanyName.MyProjectName @@ -16,7 +18,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpPermissionManagementDomainIdentityModule), typeof(AbpAuditingModule), typeof(BackgroundJobsDomainModule), - typeof(AbpAuditLoggingDomainModule) + typeof(AbpAuditLoggingDomainModule), + typeof(AbpTenantManagementDomainModule) )] public class MyProjectNameDomainModule : AbpModule { @@ -34,6 +37,11 @@ namespace MyCompanyName.MyProjectName .AddBaseTypes(typeof(AbpValidationResource)) .AddVirtualJson("/Localization/MyProjectName"); }); + + Configure(options => + { + options.IsEnabled = true; + }); } } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContext.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContext.cs index 5fe35526b0..446e6b6ba4 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContext.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContext.cs @@ -6,6 +6,7 @@ using Volo.Abp.Identity; using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.EntityFrameworkCore { @@ -28,6 +29,7 @@ namespace MyCompanyName.MyProjectName.EntityFrameworkCore builder.ConfigureBackgroundJobs(); builder.ConfigureAuditLogging(); builder.ConfigureIdentity(); + builder.ConfigureTenantManagement(); /* Configure customizations for entities from the modules included */ diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.Designer.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs similarity index 89% rename from templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.Designer.cs rename to templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs index b25ddbee04..4244444813 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.Designer.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs @@ -10,7 +10,7 @@ using MyCompanyName.MyProjectName.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(MyProjectNameMigrationsDbContext))] - [Migration("20190320072839_Initial")] + [Migration("20190402131334_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -84,6 +84,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("TenantId") .HasColumnName("TenantId"); + b.Property("TenantName"); + b.Property("Url") .HasColumnName("Url") .HasMaxLength(256); @@ -163,6 +165,8 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("EntityId") .HasMaxLength(128); + b.Property("EntityTenantId"); + b.Property("EntityTypeFullName") .IsRequired() .HasColumnName("EntityTypeFullName") @@ -611,6 +615,69 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpSettings"); }); + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId"); + + b.Property("Name") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => { b.HasOne("Volo.Abp.AuditLogging.AuditLog") @@ -679,6 +746,14 @@ namespace MyCompanyName.MyProjectName.Migrations .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant") + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade); + }); #pragma warning restore 612, 618 } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs similarity index 90% rename from templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.cs rename to templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs index 8f177b29fb..dcba749aed 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs @@ -18,6 +18,7 @@ namespace MyCompanyName.MyProjectName.Migrations UserId = table.Column(nullable: true), UserName = table.Column(maxLength: 256, nullable: true), TenantId = table.Column(nullable: true), + TenantName = table.Column(nullable: true), ImpersonatorUserId = table.Column(nullable: true), ImpersonatorTenantId = table.Column(nullable: true), ExecutionTime = table.Column(nullable: false), @@ -128,6 +129,27 @@ namespace MyCompanyName.MyProjectName.Migrations table.PrimaryKey("PK_AbpSettings", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpTenants", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + CreationTime = table.Column(nullable: false), + CreatorId = table.Column(nullable: true), + LastModificationTime = table.Column(nullable: true), + LastModifierId = table.Column(nullable: true), + IsDeleted = table.Column(nullable: false, defaultValue: false), + DeleterId = table.Column(nullable: true), + DeletionTime = table.Column(nullable: true), + Name = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpUsers", columns: table => new @@ -198,6 +220,7 @@ namespace MyCompanyName.MyProjectName.Migrations TenantId = table.Column(nullable: true), ChangeTime = table.Column(nullable: false), ChangeType = table.Column(nullable: false), + EntityTenantId = table.Column(nullable: true), EntityId = table.Column(maxLength: 128, nullable: false), EntityTypeFullName = table.Column(maxLength: 128, nullable: false), ExtraProperties = table.Column(nullable: true) @@ -234,6 +257,25 @@ namespace MyCompanyName.MyProjectName.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpTenantConnectionStrings", + columns: table => new + { + TenantId = table.Column(nullable: false), + Name = table.Column(maxLength: 64, nullable: false), + Value = table.Column(maxLength: 1024, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenantConnectionStrings", x => new { x.TenantId, x.Name }); + table.ForeignKey( + name: "FK_AbpTenantConnectionStrings_AbpTenants_TenantId", + column: x => x.TenantId, + principalTable: "AbpTenants", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserClaims", columns: table => new @@ -405,6 +447,12 @@ namespace MyCompanyName.MyProjectName.Migrations table: "AbpSettings", columns: new[] { "Name", "ProviderName", "ProviderKey" }); + migrationBuilder.CreateIndex( + name: "IX_AbpTenants_Name", + table: "AbpTenants", + column: "Name", + unique: true); + migrationBuilder.CreateIndex( name: "IX_AbpUserClaims_UserId", table: "AbpUserClaims", @@ -464,6 +512,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpSettings"); + migrationBuilder.DropTable( + name: "AbpTenantConnectionStrings"); + migrationBuilder.DropTable( name: "AbpUserClaims"); @@ -479,6 +530,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpEntityChanges"); + migrationBuilder.DropTable( + name: "AbpTenants"); + migrationBuilder.DropTable( name: "AbpRoles"); diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs index 43def4fbad..f6136bdfbd 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs @@ -82,6 +82,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("TenantId") .HasColumnName("TenantId"); + b.Property("TenantName"); + b.Property("Url") .HasColumnName("Url") .HasMaxLength(256); @@ -161,6 +163,8 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("EntityId") .HasMaxLength(128); + b.Property("EntityTenantId"); + b.Property("EntityTypeFullName") .IsRequired() .HasColumnName("EntityTypeFullName") @@ -609,6 +613,69 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpSettings"); }); + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId"); + + b.Property("Name") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => { b.HasOne("Volo.Abp.AuditLogging.AuditLog") @@ -677,6 +744,14 @@ namespace MyCompanyName.MyProjectName.Migrations .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant") + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade); + }); #pragma warning restore 612, 618 } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations.csproj index b38b78dbd2..773870d3de 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations.csproj @@ -7,6 +7,10 @@ MyCompanyName.MyProjectName + + + + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreModule.cs index efd0a9204f..4cc9a0a379 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreModule.cs @@ -6,6 +6,7 @@ using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.EntityFrameworkCore { @@ -16,7 +17,8 @@ namespace MyCompanyName.MyProjectName.EntityFrameworkCore typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpEntityFrameworkCoreSqlServerModule), typeof(BackgroundJobsEntityFrameworkCoreModule), - typeof(AbpAuditLoggingEntityFrameworkCoreModule) + typeof(AbpAuditLoggingEntityFrameworkCoreModule), + typeof(AbpTenantManagementEntityFrameworkCoreModule) )] public class MyProjectNameEntityFrameworkCoreModule : AbpModule { diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj index 30b78df34e..2776aebb0e 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj @@ -14,6 +14,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbModule.cs index 17bccd4f17..e16cb6238f 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbModule.cs @@ -5,6 +5,7 @@ using Volo.Abp.Identity.MongoDB; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.MongoDB; using Volo.Abp.SettingManagement.MongoDB; +using Volo.Abp.TenantManagement.MongoDb; namespace MyCompanyName.MyProjectName.MongoDb { @@ -13,7 +14,8 @@ namespace MyCompanyName.MyProjectName.MongoDb typeof(AbpSettingManagementMongoDbModule), typeof(AbpIdentityMongoDbModule), typeof(BackgroundJobsMongoDbModule), - typeof(AbpAuditLoggingMongoDbModule) + typeof(AbpAuditLoggingMongoDbModule), + typeof(AbpTenantManagementMongoDbModule) )] public class MyProjectNameMongoDbModule : AbpModule { diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj index ece0fd1b4d..01e5c837a4 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj @@ -14,6 +14,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index f3e67ec343..6866fb16c0 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -34,6 +34,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs index 08ccecdf38..97357b8ceb 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs @@ -34,6 +34,8 @@ using Volo.Abp.VirtualFileSystem; using Volo.Abp.PermissionManagement; // using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.TenantManagement.Web; + // namespace MyCompanyName.MyProjectName @@ -44,7 +46,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpAutofacModule), typeof(AbpIdentityWebModule), typeof(AbpAccountWebModule), - typeof(AbpAspNetCoreMvcUiBasicThemeModule) + typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(AbpTenantManagementWebModule) )] public class MyProjectNameWebModule : AbpModule { From c30baa39fddbb4b06cbe38ca9cec84e048ad8f0a Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 2 Apr 2019 16:21:03 +0300 Subject: [PATCH 027/116] Update tr.json --- .../Localization/Resources/AbpAccount/Web/tr.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json index 54ae7b0016..dfc1c9486a 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json +++ b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json @@ -11,12 +11,12 @@ "InvalidUserNameOrPassword": "Kullanıcı adı ya da şifre geçersiz!", "LoginIsNotAllowed": "Giriş yapamazsınız! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.", "SelfRegistrationDisabledMessage": "Bu uygulama için kullanıcıların kendi kendilerine kaydolmaları engellenmiştir. Yeni bir kullanıcı kaydetmek için lütfen uygulama yöneticisi ile iletişime geçin.", - "Login": "Login", - "Cancel": "Cancel", - "Register": "Register", - "UseAnotherServiceToLogIn.": "Use another service to log in.", - "InvalidLoginRequest": "Invalid login request", - "ThereAreNoLoginSchemesConfiguredForThisClient": "There are no login schemes configured for this client.", - "LogInUsingYourProviderAccount": "Log in using your {0} account" + "Login": "Giriş yap", + "Cancel": "İptal", + "Register": "Kayıt ol", + "UseAnotherServiceToLogIn.": "Başka bir servisle giriş yap.", + "InvalidLoginRequest": "Başarısız giriş isteği", + "ThereAreNoLoginSchemesConfiguredForThisClient": "Bu client için konfigüre edilmiş giriş şeması bulunamadı.", + "LogInUsingYourProviderAccount": "{0} hesabınızla giriş yapın." } } \ No newline at end of file From ee0f7a5eeb9637cd7a6d2b6e0f5dd271aaa044f8 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Tue, 2 Apr 2019 16:48:46 +0300 Subject: [PATCH 028/116] Added bug report template --- docs/en/Contribution/Index.md | 85 +++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/docs/en/Contribution/Index.md b/docs/en/Contribution/Index.md index 67fbc4ce96..816f1b045a 100644 --- a/docs/en/Contribution/Index.md +++ b/docs/en/Contribution/Index.md @@ -1,52 +1,79 @@ -## Contribution Guide +**DRAFT**: This doc has been created as a draft. +See https://help.github.com/en/articles/setting-guidelines-for-repository-contributors -ABP is an [open source](https://github.com/abpframework) and community driven project. This guide is aims to help anyone wants to contribute to the project. +# Contributing to ASP.NET Boilerplate -### Code Contribution +ASP.NET Boilerplate is an [open source](https://github.com/aspnetboilerplate/aspnetboilerplate) and community driven project. This guide is aims to help anyone wants to contribute to the project. -You can always send pull requests to the Github repository. +## Code Contribution -- Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. +You can always send pull requests to the GitHub repository. + +- Clone the [ASP.NET Boilerplate repository](https://github.com/aspnetboilerplate/aspnetboilerplate/) from GitHub. - Make the required changes. - Send a pull request. -Before making any change, please discuss it on the [Github issues](https://github.com/abpframework/abp/issues). In this way, no other developer will work on the same issue and your PR will have a better chance to be accepted. +Before making any change, please discuss it on the [GitHub issues page](https://github.com/aspnetboilerplate/aspnetboilerplate/issues). So that, other developers will not work on the same issue and your PR will have a better chance to be accepted. + +### Bug Fixes & Enhancements + +You may want to fix a known bug or work on a planned enhancement. See [the issue list](https://github.com/aspnetboilerplate/aspnetboilerplate/issues) on GitHub. + +### Feature Requests + +If you have a feature idea for the framework or modules, [create an issue](https://github.com/aspnetboilerplate/aspnetboilerplate/issues/new) on GitHub or attend to an existing discussion. Then you can implement it if it's embraced by the community. + +## Document Contribution + +You may want to improve the [documentation](https://aspnetboilerplate.com/Pages/Documents). If so, follow these steps: + +* Clone the [ABP repository](https://github.com/aspnetboilerplate/aspnetboilerplate/) from GitHub. +* Documents are located in [/aspnetboilerplate/doc](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/master/doc/WebSite) folder. +* Modify the documents and send pull request +* If you would like to add a new document, you need to add it to the navigation document as well. Navigation document is located in [doc/WebSite/Navigation.md](https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/doc/WebSite/Navigation.md). + +## Resource Localization + +ASP.NET Boilerplate framework has a [localization system](https://aspnetboilerplate.com/Pages/Documents/Localization). Localization resources are located in [Abp\Localization\Sources\AbpXmlSource](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/dev/src/Abp/Localization/Sources/AbpXmlSource). +You can add a new translation or update existing ones. +To add missing translation, see [this example pull request](https://github.com/aspnetboilerplate/aspnetboilerplate/pull/2471) + +## Writing a New Module + +The framework has pre-build modules, you can also add a new module. [Abp.Dapper](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/dev/src/Abp.Dapper) is a contributed module. You can check Abp.Dapper module to make your own. -#### Bug Fixes & Enhancements +TODO: May be added step by step module development guide. -You may want to fix a known bug or work on a planned enhancement. See [the issue list](https://github.com/abpframework/abp/issues) on Github. +## Blog Posts & Tutorials -#### Feature Requests +If you would like to write tutorials or blog posts for ASP.NET Boilerplate, please let us know (by creating a GitHub issue](https://github.com/aspnetboilerplate/aspnetboilerplate/issues), so we may add a link to your tutorial/post in the official documentation and we announce it on the official [Twitter account](https://twitter.com/aspboilerplate). -If you have a feature idea for the framework or modules, [create an issue](https://github.com/abpframework/abp/issues/new) on Github or attend to an existing discussion. Then you can implement it if it's embraced by the community. +## Bug Report -### Document Translation +If you would like to report a bug, please [create an issue on the GitHub repository](https://github.com/aspnetboilerplate/aspnetboilerplate/issues/new) -You may want to translate the complete [documentation](https://abp.io/documents/) (including this one) to your mother language. If so, follow these steps: +You need to fill out the issue template before posting a bug. -* Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. -* To add a new language, create a new folder inside the [docs](https://github.com/abpframework/abp/tree/master/docs) folder. Folder names can be "en", "es", "fr", "tr" and so on based on the language (see [all culture codes](https://msdn.microsoft.com/en-us/library/hh441729.aspx)). -* Get the ["en" folder](https://github.com/abpframework/abp/tree/master/docs/en) as a reference for the file names and folder structure. Keep the same naming if you are translating the same documentation. -* Send a pull request (PR) once you translate any document. Please translate documents & send PRs one by one. Don't wait to finish translations for all documents. +```markdown +### GitHub Issues -### Resource Localization +GitHub issues are for bug reports, feature requests and other discussions about the framework. -ABP framework has a flexible [localization system](https://abp.io/documents/abp/latest/Localization). You can create localized user interfaces for your own application. +If you're creating a bug/problem report, please include followings: -In addition to that, the framework and pre-build modules have already localized texts. As an example, see [the localization texts for the Volo.Abp.UI package](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). You can create a new file in the [same folder](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) to translate it. +* Your Abp package version. +* Your base framework: .Net Framework or .Net Core. +* Exception message and stack trace if available. +* Steps needed to reproduce the problem. -* Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. -* Create a new file for the target language for a localization text (json) file (near to the en.json file). -* Copy all texts from the en.json file. -* Translate the texts. -* Send pull request on Github. +Please write in English. -ABP is a modular framework. So there are many localization text resource, one per module. To find all .json files, you can search for "en.json" after cloning the repository. You can also check [this list](Localization-Text-Files.md) for a list of localization text files. +### Stack Overflow -### Blog Posts & Tutorials +Please use Stack Overflow for your questions about using the framework, templates and samples: -If you decide to create some tutorials or blog posts on ABP, please inform us (by creating a [Github issue](https://github.com/abpframework/abp/issues)), so we may add a link to your tutorial/post in the official documentation and we can announce it on our [Twitter account](https://twitter.com/abpframework). +https://stackoverflow.com/questions/tagged/aspnetboilerplate -### Bug Report +Use aspnetboilerplate tag in your questions. -If you find any bug, please [create an issue on the Github repository](https://github.com/abpframework/abp/issues/new). \ No newline at end of file +``` From ce3c42af5e27aacefa3a6a6183517458b056d93b Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 2 Apr 2019 16:49:51 +0300 Subject: [PATCH 029/116] Resolved #951: Create Tenant switch UI for the basic theme --- .../AbpAspNetCoreMvcUIBasicThemeModule.cs | 4 ++- .../Themes/Basic/Layouts/Account.cshtml | 30 ++++++++++++++++++- ...o.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj | 1 + 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs index 08d04cfed2..d6e4935321 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs @@ -1,4 +1,5 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Toolbars; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; @@ -11,7 +12,8 @@ using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic { [DependsOn( - typeof(AbpAspNetCoreMvcUiThemeSharedModule) + typeof(AbpAspNetCoreMvcUiThemeSharedModule), + typeof(AbpAspNetCoreMvcUiMultiTenancyModule) )] public class AbpAspNetCoreMvcUiBasicThemeModule : AbpModule { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml index 8456d31c58..1a305434db 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml @@ -1,10 +1,20 @@ -@using Volo.Abp.AspNetCore.Mvc.AntiForgery +@using Microsoft.Extensions.Localization +@using Microsoft.Extensions.Options +@using Volo.Abp.AspNetCore.MultiTenancy +@using Volo.Abp.AspNetCore.Mvc.AntiForgery +@using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.Localization @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.MainNavbar @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.PageAlerts @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components +@using Volo.Abp.MultiTenancy @inject IAbpAntiForgeryManager AbpAntiForgeryManager @inject IBrandingProvider BrandingProvider +@inject IOptions MultiTenancyOptions +@inject ICurrentTenant CurrentTenant +@inject IStringLocalizer MultiTenancyStringLocalizer +@inject ITenantResolveResultAccessor TenantResolveResultAccessor + @{ Layout = null; AbpAntiForgeryManager.SetCookie(); @@ -32,6 +42,24 @@
+ @if (MultiTenancyOptions.Value.IsEnabled && + (TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true)) + { +
+ @MultiTenancyStringLocalizer["Tenant"]: + + @if (CurrentTenant.Id == null) + { + @MultiTenancyStringLocalizer["NotSelected"] + } + else + { + @(CurrentTenant.Name ?? CurrentTenant.Id.Value.ToString()) + } + + (@MultiTenancyStringLocalizer["Switch"]) +
+ } @(await Component.InvokeAsync()) @RenderBody()
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj index ff7bdd4b1e..654b191bec 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj @@ -31,6 +31,7 @@ + From cf3795a8a4f014718d3fe810610cf1dd97c6f8e0 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 2 Apr 2019 16:51:10 +0300 Subject: [PATCH 030/116] Add MyProjectNameConsts.IsMultiTenancyEnabled constant. --- .../Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs | 9 +++++++++ .../MyProjectNameConsts.cs | 2 ++ .../MyProjectNameDomainModule.cs | 2 +- .../Menus/MyProjectNameMenuContributor.cs | 7 +++++++ .../MyProjectNameWebModule.cs | 6 ++++++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs index e69471e15d..b7aa7f97cf 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs @@ -39,6 +39,15 @@ namespace Volo.Abp.UI.Navigation return menuWithItems.Items.FirstOrDefault(mi => mi.Name == menuItemName); } + public static bool TryRemoveMenuItem( + [NotNull] this IHasMenuItems menuWithItems, + string menuItemName) + { + Check.NotNull(menuWithItems, nameof(menuWithItems)); + + return menuWithItems.Items.RemoveAll(item => item.Name == menuItemName) > 0; + } + [NotNull] public static IHasMenuItems SetSubItemOrder( [NotNull] this IHasMenuItems menuWithItems, diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs index d939cfca9f..f28dd57091 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs @@ -5,5 +5,7 @@ public const string DbTablePrefix = "App"; public const string DbSchema = null; + + public const bool IsMultiTenancyEnabled = false; } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs index 9717644f7a..95f46011e8 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs @@ -40,7 +40,7 @@ namespace MyCompanyName.MyProjectName Configure(options => { - options.IsEnabled = true; + options.IsEnabled = MyProjectNameConsts.IsMultiTenancyEnabled; }); } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs index 1d8069465c..479618f160 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using MyCompanyName.MyProjectName.Localization.MyProjectName; +using Volo.Abp.TenantManagement.Web.Navigation; using Volo.Abp.UI.Navigation; namespace MyCompanyName.MyProjectName.Menus @@ -18,6 +19,12 @@ namespace MyCompanyName.MyProjectName.Menus private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) { + if (!MyProjectNameConsts.IsMultiTenancyEnabled) + { + ApplicationMenuItem administration = context.Menu.GetAdministration(); + administration.TryRemoveMenuItem(TenantManagementMenuNames.GroupName); + } + var l = context.ServiceProvider.GetRequiredService>(); context.Menu.Items.Insert(0, new ApplicationMenuItem("MyProjectName.Home", l["Menu:Home"], "/")); diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs index 97357b8ceb..330cf78fe2 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs @@ -178,6 +178,12 @@ namespace MyCompanyName.MyProjectName app.UseVirtualFiles(); app.UseAuthentication(); + + if (MyProjectNameConsts.IsMultiTenancyEnabled) + { + app.UseMultiTenancy(); + } + app.UseAbpRequestLocalization(); app.UseSwagger(); From 9df111d3075265a10ebe8cfd1a66cd7b606a792a Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 2 Apr 2019 17:12:39 +0300 Subject: [PATCH 031/116] Update tr.json --- framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json index a3af7c3a22..cd4a3c0565 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json @@ -40,7 +40,7 @@ "PagerInfo": "_TOTAL_ kayıttan _START_ ile _END_ arası gösteriliyor.", "PagerInfoEmpty": "0 kayıttan 0 ile 0 arası gösteriliyor.", "PagerInfoFiltered": "(_MAX_ kayıt arasından filtrelendi)", - "NoDataAvailableInDatatable": "Tabloda kayır mevcut değil.", + "NoDataAvailableInDatatable": "Tabloda kayıt mevcut değil.", "PagerShowMenuEntries": "Sayfada _MENU_ kayıt göster.", "DatatableActionDropdownDefaultText": "İşlemler", "ChangePassword": "Şifre değiştir", From 5387ac43f08ea731dd19454d202095c8f688b4bd Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 2 Apr 2019 17:26:03 +0300 Subject: [PATCH 032/116] Add feature management module to the mvc template #950 --- .../MyCompanyName.MyProjectName.Application.csproj | 1 + .../MyProjectNameApplicationModule.cs | 4 +++- .../MyCompanyName.MyProjectName.Domain.csproj | 1 + .../MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs | 2 +- .../MyProjectNameDomainModule.cs | 4 +++- .../MyProjectNameEntityFrameworkCoreModule.cs | 4 +++- .../MyCompanyName.MyProjectName.EntityFrameworkCore.csproj | 1 + .../MongoDb/MyProjectNameMongoDbModule.cs | 4 +++- .../MyCompanyName.MyProjectName.MongoDB.csproj | 1 + .../MyCompanyName.MyProjectName.Web.csproj | 1 + 10 files changed, 18 insertions(+), 5 deletions(-) diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj index 56ba7e68ae..24989a390b 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj @@ -12,6 +12,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs index 3373432a4c..861076f3e8 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs @@ -1,4 +1,5 @@ using Volo.Abp.AutoMapper; +using Volo.Abp.FeatureManagement; using Volo.Abp.Identity; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement; @@ -10,7 +11,8 @@ namespace MyCompanyName.MyProjectName typeof(MyProjectNameDomainModule), typeof(AbpIdentityApplicationModule), typeof(AbpPermissionManagementApplicationModule), - typeof(AbpTenantManagementApplicationModule) + typeof(AbpTenantManagementApplicationModule), + typeof(AbpFeatureManagementApplicationModule) )] public class MyProjectNameApplicationModule : AbpModule { diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj index 0b829f04d8..c9e416ca66 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj @@ -13,6 +13,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs index f28dd57091..9082f904ce 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs @@ -6,6 +6,6 @@ public const string DbSchema = null; - public const bool IsMultiTenancyEnabled = false; + public const bool IsMultiTenancyEnabled = true; } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs index 95f46011e8..7484a0c94d 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs @@ -2,6 +2,7 @@ using Volo.Abp.Auditing; using Volo.Abp.AuditLogging; using Volo.Abp.BackgroundJobs; +using Volo.Abp.FeatureManagement; using Volo.Abp.Identity; using Volo.Abp.Localization; using Volo.Abp.Localization.Resources.AbpValidation; @@ -19,7 +20,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpAuditingModule), typeof(BackgroundJobsDomainModule), typeof(AbpAuditLoggingDomainModule), - typeof(AbpTenantManagementDomainModule) + typeof(AbpTenantManagementDomainModule), + typeof(AbpFeatureManagementDomainModule) )] public class MyProjectNameDomainModule : AbpModule { diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreModule.cs index 4cc9a0a379..5f9bd98bcf 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreModule.cs @@ -2,6 +2,7 @@ using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.BackgroundJobs.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.SqlServer; +using Volo.Abp.FeatureManagement.EntityFrameworkCore; using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; @@ -18,7 +19,8 @@ namespace MyCompanyName.MyProjectName.EntityFrameworkCore typeof(AbpEntityFrameworkCoreSqlServerModule), typeof(BackgroundJobsEntityFrameworkCoreModule), typeof(AbpAuditLoggingEntityFrameworkCoreModule), - typeof(AbpTenantManagementEntityFrameworkCoreModule) + typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpFeatureManagementEntityFrameworkCoreModule) )] public class MyProjectNameEntityFrameworkCoreModule : AbpModule { diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj index 2776aebb0e..97abb2f5e4 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj @@ -15,6 +15,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbModule.cs index e16cb6238f..553c799f2c 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbModule.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AuditLogging.MongoDB; using Volo.Abp.BackgroundJobs.MongoDB; +using Volo.Abp.FeatureManagement.MongoDB; using Volo.Abp.Identity.MongoDB; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.MongoDB; @@ -15,7 +16,8 @@ namespace MyCompanyName.MyProjectName.MongoDb typeof(AbpIdentityMongoDbModule), typeof(BackgroundJobsMongoDbModule), typeof(AbpAuditLoggingMongoDbModule), - typeof(AbpTenantManagementMongoDbModule) + typeof(AbpTenantManagementMongoDbModule), + typeof(AbpFeatureManagementMongoDbModule) )] public class MyProjectNameMongoDbModule : AbpModule { diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj index 01e5c837a4..35e72980b0 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj @@ -15,6 +15,7 @@ + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index 6866fb16c0..4f44771362 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -35,6 +35,7 @@ + From 61b9a61e2645d422ab717dfcc587101dc0e8c5ec Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 3 Apr 2019 11:11:21 +0800 Subject: [PATCH 033/116] Added bug report template. --- docs/zh-Hans/Contribution/Index.md | 89 +++++++++++++++++++----------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/docs/zh-Hans/Contribution/Index.md b/docs/zh-Hans/Contribution/Index.md index b6b9839262..745a86f968 100644 --- a/docs/zh-Hans/Contribution/Index.md +++ b/docs/zh-Hans/Contribution/Index.md @@ -1,52 +1,79 @@ -## 贡献指南 +**草案**: 该文档已作为草稿创建. +请参见 https://help.github.com/en/articles/setting-guidelines-for-repository-contributors -ABP是[开源](https://github.com/abpframework)和社区驱动项目. 本指南旨在帮助任何想要为项目做出贡献的人. +# 贡献于ASP.NET Boilerplate -### 贡献代码 +ASP.NET Boilerplate是一个[开源](https://github.com/aspnetboilerplate/aspnetboilerplate)和社区驱动的项目. 本指南旨在帮助任何人想要为项目做出贡献. -你可以将Pull request(拉取请求)发送到Github存储库. +## 代码贡献 -- 从Github克隆[ABP存储库](https://github.com/abpframework/abp/). -- 进行必要的更改. -- 发送Pull request(拉取请求). +你始终可以将PR(Pull Request)发送到GitHub存储库. -在进行任何更改之前,请在[Github问题](https://github.com/abpframework/abp/issues)上进行讨论. 通过这种方式, 其他开发人员将不会处理同一个问题, 你的PR将有更好的机会被接受. + - 从GitHub克隆[ASP.NET Boilerplate存储库](https://github.com/aspnetboilerplate/aspnetboilerplate/). + - 进行必要的更改. + - 发送PR(Pull Request). -#### Bug修复 & 增强功能 +在进行任何更改之前,请在[GitHub Issue页面]((ttps://github.com/aspnetboilerplate/aspnetboilerplate/issues)上进行讨论. 因此,其他开发人员不会处理相同的问题, 你的PR将有更好的机会被接受. -你可能希望修复已知Bug或处理计划的增强功能. 请参阅Github上的[问题列表](https://github.com/abpframework/abp/issues). +### 修复BUG和增强功能 -#### 功能请求 +你可能想要修复已知BUG或处理计划的增强功能. 请参阅GitHub上的[Issue列表](https://github.com/aspnetboilerplate/aspnetboilerplate/issues) -如果你对框架或模块有功能的想法, 请在Github上[创建一个问题](https://github.com/abpframework/abp/issues/new)或参加现有的讨论. 如果它被社区所接受你就可以实现它. +### 功能请求 -### 文件翻译 +如果你有一个关于框架或模块的功能想法,在GitHub上创建一个Issue(https://github.com/aspnetboilerplate/aspnetboilerplate/issues/new)或参加现有的讨论. 然后,如果它被社区所接受,你就可以实施它. -你可能希望将完整的[文档](https://abp.io/documents/)(包括本文)翻译成你的母语. 请按照下列步骤操作: +## 文档贡献 -* 从Github克隆[ABP存储库](https://github.com/abpframework/abp/). -* 要添加新语言,请在[docs](https://github.com/abpframework/abp/tree/master/docs)文件夹中创建一个新文件夹. 文件夹名称可以是" en","es","fr","tr"等(参见[所有文化代码](https://msdn.microsoft.com/en-us/library/hh441729.aspx)). -* 获取["en"文件夹](https://github.com/abpframework/abp/tree/master/docs/en)作为文件名和文件夹结构的参考. 如果要翻译相同的文档, 请保持相同的命名. -* 翻译任何文档后发送拉取请求(PR). 请翻译文件后及时发送PR. 不要等到完成所有文件的翻译. +你可能希望改进[文档](https://aspnetboilerplate.com/Pages/Documents). 如果是,请按照下列步骤操作: -### 资源本地化 +* 从GitHub克隆[ABP存储库](https://github.com/aspnetboilerplate/aspnetboilerplate/). +* 文档位于 [/aspnetboilerplate/doc](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/master/doc/WebSite)文件夹中. +* 修改文件并发送PR(Pull Request). +* 如果要添加新文档,还需要将其添加到导航文档中. 导航文档位于[doc/WebSite/Navigation.md](https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/doc/WebSite/Navigation.md). -ABP框架具有灵活的[本地化系统](https://abp.io/documents/abp/latest/Localization). 你可以为自己的应用程序创建本地化用户界面. +## 资源本地化 -除此之外,框架和预构建模块已经本地化了文本.请参阅[Volo.Abp.UI包的本地化文本](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json).你可以在[相同文件夹](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi)中创建一个新文件进行翻译. +ASP.NET Boilerplate框架有一个[本地化系统](https://aspnetboilerplate.com/Pages/Documents/Localization). 本地化资源位于[Abp\Localization\Sources\AbpXmlSource](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/dev/src/Abp/Localization/Sources/AbpXmlSource). +你可以添加新翻译或更新现有翻译. +要添加缺少的翻译,请参阅[此示例请求](https://github.com/aspnetboilerplate/aspnetboilerplate/pull/2471) -* 从Github克隆[ABP存储库](https://github.com/abpframework/abp/). -* 为本地化文本(json)文件(en.json文件同目录下)创建目标语言的新文件. -* 复制en.json文件中的所有文本. -* 翻译文本. -* 在Github上发送拉取请求(Pull request). +## 编写新模块 -ABP是一个模块化框架. 所以有很多本地化文本资源, 每个模块都有一个. 要查找所有.json文件,可以在克隆存储库后搜索"en.json". 你还可以检查[此列表](Localization-Text-Files.md)以获取本地化文本文件列表. +该框架具有预构建模块, 你还可以添加新模块. [Abp.Dapper](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/dev/src/Abp.Dapper)是一个贡献模块. 你可以检查Abp.Dapper模块来制作你自己的模块. -### 博客文章和教程 +TODO:可以逐步添加模块开发指南. -如果你发布了一些ABP框架的教程或博客帖子, 请通知我们(通过创建[Github问题](https://github.com/abpframework/abp/issues)), 我们可能会在官方文档中添加指向你的教程或博客帖子的链接和在[推特](https://twitter.com/abpframework)上公布. +## 博客文章和教程 -### Bug 报告 +如果你想为ASP.NET Boilerplate编写教程或博客文章,请告诉我们(通过创建GitHub Issue)(https://github.com/aspnetboilerplate/aspnetboilerplate/issues), 以便我们添加链接到你的教程/帖子在官方文档中,我们在官方[Twitter帐户](https://twitter.com/aspboilerplate). -如果你发现任何Bug, 请[在Github存储库上创建一个问题](https://github.com/abpframework/abp/issues/new). \ No newline at end of file +## 报告BUG + +如果您想报告BUG, 请[在GitHub存储库上创建一个Issue](https://github.com/aspnetboilerplate/aspnetboilerplate/issues/new) + +你需要在发布BUG之前填写问题模板. + +```markdown +### GitHub Issues + +GitHub issues are for bug reports, feature requests and other discussions about the framework. + +If you're creating a bug/problem report, please include followings: + +* Your Abp package version. +* Your base framework: .Net Framework or .Net Core. +* Exception message and stack trace if available. +* Steps needed to reproduce the problem. + +Please write in English. + +### Stack Overflow + +Please use Stack Overflow for your questions about using the framework, templates and samples: + +https://stackoverflow.com/questions/tagged/aspnetboilerplate + +Use aspnetboilerplate tag in your questions. + +``` From ec6379c68982544b57fc33b3a3d58da1dd934b5e Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 3 Apr 2019 11:11:21 +0800 Subject: [PATCH 034/116] Revert "Added bug report template." This reverts commit 61b9a61e2645d422ab717dfcc587101dc0e8c5ec. --- docs/zh-Hans/Contribution/Index.md | 89 +++++++++++------------------- 1 file changed, 31 insertions(+), 58 deletions(-) diff --git a/docs/zh-Hans/Contribution/Index.md b/docs/zh-Hans/Contribution/Index.md index 745a86f968..b6b9839262 100644 --- a/docs/zh-Hans/Contribution/Index.md +++ b/docs/zh-Hans/Contribution/Index.md @@ -1,79 +1,52 @@ -**草案**: 该文档已作为草稿创建. -请参见 https://help.github.com/en/articles/setting-guidelines-for-repository-contributors +## 贡献指南 -# 贡献于ASP.NET Boilerplate +ABP是[开源](https://github.com/abpframework)和社区驱动项目. 本指南旨在帮助任何想要为项目做出贡献的人. -ASP.NET Boilerplate是一个[开源](https://github.com/aspnetboilerplate/aspnetboilerplate)和社区驱动的项目. 本指南旨在帮助任何人想要为项目做出贡献. +### 贡献代码 -## 代码贡献 +你可以将Pull request(拉取请求)发送到Github存储库. -你始终可以将PR(Pull Request)发送到GitHub存储库. +- 从Github克隆[ABP存储库](https://github.com/abpframework/abp/). +- 进行必要的更改. +- 发送Pull request(拉取请求). - - 从GitHub克隆[ASP.NET Boilerplate存储库](https://github.com/aspnetboilerplate/aspnetboilerplate/). - - 进行必要的更改. - - 发送PR(Pull Request). +在进行任何更改之前,请在[Github问题](https://github.com/abpframework/abp/issues)上进行讨论. 通过这种方式, 其他开发人员将不会处理同一个问题, 你的PR将有更好的机会被接受. -在进行任何更改之前,请在[GitHub Issue页面]((ttps://github.com/aspnetboilerplate/aspnetboilerplate/issues)上进行讨论. 因此,其他开发人员不会处理相同的问题, 你的PR将有更好的机会被接受. +#### Bug修复 & 增强功能 -### 修复BUG和增强功能 +你可能希望修复已知Bug或处理计划的增强功能. 请参阅Github上的[问题列表](https://github.com/abpframework/abp/issues). -你可能想要修复已知BUG或处理计划的增强功能. 请参阅GitHub上的[Issue列表](https://github.com/aspnetboilerplate/aspnetboilerplate/issues) +#### 功能请求 -### 功能请求 +如果你对框架或模块有功能的想法, 请在Github上[创建一个问题](https://github.com/abpframework/abp/issues/new)或参加现有的讨论. 如果它被社区所接受你就可以实现它. -如果你有一个关于框架或模块的功能想法,在GitHub上创建一个Issue(https://github.com/aspnetboilerplate/aspnetboilerplate/issues/new)或参加现有的讨论. 然后,如果它被社区所接受,你就可以实施它. +### 文件翻译 -## 文档贡献 +你可能希望将完整的[文档](https://abp.io/documents/)(包括本文)翻译成你的母语. 请按照下列步骤操作: -你可能希望改进[文档](https://aspnetboilerplate.com/Pages/Documents). 如果是,请按照下列步骤操作: +* 从Github克隆[ABP存储库](https://github.com/abpframework/abp/). +* 要添加新语言,请在[docs](https://github.com/abpframework/abp/tree/master/docs)文件夹中创建一个新文件夹. 文件夹名称可以是" en","es","fr","tr"等(参见[所有文化代码](https://msdn.microsoft.com/en-us/library/hh441729.aspx)). +* 获取["en"文件夹](https://github.com/abpframework/abp/tree/master/docs/en)作为文件名和文件夹结构的参考. 如果要翻译相同的文档, 请保持相同的命名. +* 翻译任何文档后发送拉取请求(PR). 请翻译文件后及时发送PR. 不要等到完成所有文件的翻译. -* 从GitHub克隆[ABP存储库](https://github.com/aspnetboilerplate/aspnetboilerplate/). -* 文档位于 [/aspnetboilerplate/doc](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/master/doc/WebSite)文件夹中. -* 修改文件并发送PR(Pull Request). -* 如果要添加新文档,还需要将其添加到导航文档中. 导航文档位于[doc/WebSite/Navigation.md](https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/doc/WebSite/Navigation.md). +### 资源本地化 -## 资源本地化 +ABP框架具有灵活的[本地化系统](https://abp.io/documents/abp/latest/Localization). 你可以为自己的应用程序创建本地化用户界面. -ASP.NET Boilerplate框架有一个[本地化系统](https://aspnetboilerplate.com/Pages/Documents/Localization). 本地化资源位于[Abp\Localization\Sources\AbpXmlSource](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/dev/src/Abp/Localization/Sources/AbpXmlSource). -你可以添加新翻译或更新现有翻译. -要添加缺少的翻译,请参阅[此示例请求](https://github.com/aspnetboilerplate/aspnetboilerplate/pull/2471) +除此之外,框架和预构建模块已经本地化了文本.请参阅[Volo.Abp.UI包的本地化文本](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json).你可以在[相同文件夹](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi)中创建一个新文件进行翻译. -## 编写新模块 +* 从Github克隆[ABP存储库](https://github.com/abpframework/abp/). +* 为本地化文本(json)文件(en.json文件同目录下)创建目标语言的新文件. +* 复制en.json文件中的所有文本. +* 翻译文本. +* 在Github上发送拉取请求(Pull request). -该框架具有预构建模块, 你还可以添加新模块. [Abp.Dapper](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/dev/src/Abp.Dapper)是一个贡献模块. 你可以检查Abp.Dapper模块来制作你自己的模块. +ABP是一个模块化框架. 所以有很多本地化文本资源, 每个模块都有一个. 要查找所有.json文件,可以在克隆存储库后搜索"en.json". 你还可以检查[此列表](Localization-Text-Files.md)以获取本地化文本文件列表. -TODO:可以逐步添加模块开发指南. +### 博客文章和教程 -## 博客文章和教程 +如果你发布了一些ABP框架的教程或博客帖子, 请通知我们(通过创建[Github问题](https://github.com/abpframework/abp/issues)), 我们可能会在官方文档中添加指向你的教程或博客帖子的链接和在[推特](https://twitter.com/abpframework)上公布. -如果你想为ASP.NET Boilerplate编写教程或博客文章,请告诉我们(通过创建GitHub Issue)(https://github.com/aspnetboilerplate/aspnetboilerplate/issues), 以便我们添加链接到你的教程/帖子在官方文档中,我们在官方[Twitter帐户](https://twitter.com/aspboilerplate). +### Bug 报告 -## 报告BUG - -如果您想报告BUG, 请[在GitHub存储库上创建一个Issue](https://github.com/aspnetboilerplate/aspnetboilerplate/issues/new) - -你需要在发布BUG之前填写问题模板. - -```markdown -### GitHub Issues - -GitHub issues are for bug reports, feature requests and other discussions about the framework. - -If you're creating a bug/problem report, please include followings: - -* Your Abp package version. -* Your base framework: .Net Framework or .Net Core. -* Exception message and stack trace if available. -* Steps needed to reproduce the problem. - -Please write in English. - -### Stack Overflow - -Please use Stack Overflow for your questions about using the framework, templates and samples: - -https://stackoverflow.com/questions/tagged/aspnetboilerplate - -Use aspnetboilerplate tag in your questions. - -``` +如果你发现任何Bug, 请[在Github存储库上创建一个问题](https://github.com/abpframework/abp/issues/new). \ No newline at end of file From ca1f2e9e8e03a2b55fbd87c454483881941f3c79 Mon Sep 17 00:00:00 2001 From: ddrsql Date: Wed, 3 Apr 2019 16:50:25 +0800 Subject: [PATCH 035/116] Docking with third-party OAuth services for ease of rewriting CurrentUser --- .../Permissions/ClientPermissionValueProvider.cs | 10 ++++++---- .../Permissions/RolePermissionValueProvider.cs | 10 +++++++--- .../Permissions/UserPermissionValueProvider.cs | 9 ++++++--- ...CurrentClaimsPrincipalTenantResolveContributor.cs | 12 ++++-------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs index 87b391443d..ec827f83a4 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs @@ -1,23 +1,25 @@ using System.Threading.Tasks; -using Volo.Abp.Security.Claims; +using Volo.Abp.Clients; namespace Volo.Abp.Authorization.Permissions { public class ClientPermissionValueProvider : PermissionValueProvider { + protected ICurrentClient CurrentClient { get; } + public const string ProviderName = "Client"; public override string Name => ProviderName; - public ClientPermissionValueProvider(IPermissionStore permissionStore) + public ClientPermissionValueProvider(ICurrentClient currentClient, IPermissionStore permissionStore) : base(permissionStore) { - + CurrentClient = currentClient; } public override async Task CheckAsync(PermissionValueCheckContext context) { - var clientId = context.Principal?.FindFirst(AbpClaimTypes.ClientId)?.Value; + var clientId = CurrentClient.Id; if (clientId == null) { diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RolePermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RolePermissionValueProvider.cs index a008190fbd..5925a3f6d4 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RolePermissionValueProvider.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RolePermissionValueProvider.cs @@ -1,24 +1,28 @@ using System.Linq; using System.Threading.Tasks; using Volo.Abp.Security.Claims; +using Volo.Abp.Users; namespace Volo.Abp.Authorization.Permissions { public class RolePermissionValueProvider : PermissionValueProvider { + protected ICurrentUser CurrentUser { get; } + public const string ProviderName = "Role"; public override string Name => ProviderName; - public RolePermissionValueProvider(IPermissionStore permissionStore) + public RolePermissionValueProvider(ICurrentUser currentUser, IPermissionStore permissionStore) : base(permissionStore) { - + CurrentUser = currentUser; } public override async Task CheckAsync(PermissionValueCheckContext context) { - var roles = context.Principal?.FindAll(AbpClaimTypes.Role).Select(c => c.Value).ToArray(); + var roles = CurrentUser.Roles; + if (roles == null || !roles.Any()) { return PermissionGrantResult.Undefined; diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/UserPermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/UserPermissionValueProvider.cs index f04a85910f..f9a6ca903e 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/UserPermissionValueProvider.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/UserPermissionValueProvider.cs @@ -1,23 +1,26 @@ using System.Threading.Tasks; using Volo.Abp.Security.Claims; +using Volo.Abp.Users; namespace Volo.Abp.Authorization.Permissions { public class UserPermissionValueProvider : PermissionValueProvider { + protected ICurrentUser CurrentUser { get; } + public const string ProviderName = "User"; public override string Name => ProviderName; - public UserPermissionValueProvider(IPermissionStore permissionStore) + public UserPermissionValueProvider(ICurrentUser currentUser, IPermissionStore permissionStore) : base(permissionStore) { - + CurrentUser = currentUser; } public override async Task CheckAsync(PermissionValueCheckContext context) { - var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value; + var userId = CurrentUser.Id.ToString(); if (userId == null) { diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentClaimsPrincipalTenantResolveContributor.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentClaimsPrincipalTenantResolveContributor.cs index 0f213ea1d7..c2e28d1222 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentClaimsPrincipalTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentClaimsPrincipalTenantResolveContributor.cs @@ -1,6 +1,5 @@ -using System.Linq; using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Security.Claims; +using Volo.Abp.Users; namespace Volo.Abp.MultiTenancy { @@ -12,17 +11,14 @@ namespace Volo.Abp.MultiTenancy public override void Resolve(ITenantResolveContext context) { - var principal = context.ServiceProvider.GetRequiredService().Principal; - if (principal?.Identity?.IsAuthenticated != true) + var currentUser = context.ServiceProvider.GetRequiredService(); + if (currentUser.IsAuthenticated != true) { return; } context.Handled = true; - context.TenantIdOrName = principal - .Claims - .FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId) - ?.Value; + context.TenantIdOrName = currentUser.TenantId.ToString(); } } } \ No newline at end of file From e338db033dafaaafec7717c8790b85e3ebde1c5a Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Wed, 3 Apr 2019 11:54:23 +0300 Subject: [PATCH 036/116] reverted back --- docs/en/Contribution/Index.md | 85 ++++++++++++----------------------- 1 file changed, 29 insertions(+), 56 deletions(-) diff --git a/docs/en/Contribution/Index.md b/docs/en/Contribution/Index.md index 816f1b045a..74174c97d1 100644 --- a/docs/en/Contribution/Index.md +++ b/docs/en/Contribution/Index.md @@ -1,79 +1,52 @@ -**DRAFT**: This doc has been created as a draft. -See https://help.github.com/en/articles/setting-guidelines-for-repository-contributors +## Contribution Guide -# Contributing to ASP.NET Boilerplate +ABP is an [open source](https://github.com/abpframework) and community driven project. This guide is aims to help anyone wants to contribute to the project. -ASP.NET Boilerplate is an [open source](https://github.com/aspnetboilerplate/aspnetboilerplate) and community driven project. This guide is aims to help anyone wants to contribute to the project. +### Code Contribution -## Code Contribution +You can always send pull requests to the Github repository. -You can always send pull requests to the GitHub repository. - -- Clone the [ASP.NET Boilerplate repository](https://github.com/aspnetboilerplate/aspnetboilerplate/) from GitHub. +- Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. - Make the required changes. - Send a pull request. -Before making any change, please discuss it on the [GitHub issues page](https://github.com/aspnetboilerplate/aspnetboilerplate/issues). So that, other developers will not work on the same issue and your PR will have a better chance to be accepted. - -### Bug Fixes & Enhancements - -You may want to fix a known bug or work on a planned enhancement. See [the issue list](https://github.com/aspnetboilerplate/aspnetboilerplate/issues) on GitHub. - -### Feature Requests - -If you have a feature idea for the framework or modules, [create an issue](https://github.com/aspnetboilerplate/aspnetboilerplate/issues/new) on GitHub or attend to an existing discussion. Then you can implement it if it's embraced by the community. - -## Document Contribution - -You may want to improve the [documentation](https://aspnetboilerplate.com/Pages/Documents). If so, follow these steps: - -* Clone the [ABP repository](https://github.com/aspnetboilerplate/aspnetboilerplate/) from GitHub. -* Documents are located in [/aspnetboilerplate/doc](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/master/doc/WebSite) folder. -* Modify the documents and send pull request -* If you would like to add a new document, you need to add it to the navigation document as well. Navigation document is located in [doc/WebSite/Navigation.md](https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/doc/WebSite/Navigation.md). - -## Resource Localization - -ASP.NET Boilerplate framework has a [localization system](https://aspnetboilerplate.com/Pages/Documents/Localization). Localization resources are located in [Abp\Localization\Sources\AbpXmlSource](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/dev/src/Abp/Localization/Sources/AbpXmlSource). -You can add a new translation or update existing ones. -To add missing translation, see [this example pull request](https://github.com/aspnetboilerplate/aspnetboilerplate/pull/2471) - -## Writing a New Module - -The framework has pre-build modules, you can also add a new module. [Abp.Dapper](https://github.com/aspnetboilerplate/aspnetboilerplate/tree/dev/src/Abp.Dapper) is a contributed module. You can check Abp.Dapper module to make your own. +Before making any change, please discuss it on the [Github issues](https://github.com/abpframework/abp/issues). In this way, no other developer will work on the same issue and your PR will have a better chance to be accepted. -TODO: May be added step by step module development guide. +#### Bug Fixes & Enhancements -## Blog Posts & Tutorials +You may want to fix a known bug or work on a planned enhancement. See [the issue list](https://github.com/abpframework/abp/issues) on Github. -If you would like to write tutorials or blog posts for ASP.NET Boilerplate, please let us know (by creating a GitHub issue](https://github.com/aspnetboilerplate/aspnetboilerplate/issues), so we may add a link to your tutorial/post in the official documentation and we announce it on the official [Twitter account](https://twitter.com/aspboilerplate). +#### Feature Requests -## Bug Report +If you have a feature idea for the framework or modules, [create an issue](https://github.com/abpframework/abp/issues/new) on Github or attend to an existing discussion. Then you can implement it if it's embraced by the community. -If you would like to report a bug, please [create an issue on the GitHub repository](https://github.com/aspnetboilerplate/aspnetboilerplate/issues/new) +### Document Translation -You need to fill out the issue template before posting a bug. +You may want to translate the complete [documentation](https://abp.io/documents/) (including this one) to your mother language. If so, follow these steps: -```markdown -### GitHub Issues +* Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. +* To add a new language, create a new folder inside the [docs](https://github.com/abpframework/abp/tree/master/docs) folder. Folder names can be "en", "es", "fr", "tr" and so on based on the language (see [all culture codes](https://msdn.microsoft.com/en-us/library/hh441729.aspx)). +* Get the ["en" folder](https://github.com/abpframework/abp/tree/master/docs/en) as a reference for the file names and folder structure. Keep the same naming if you are translating the same documentation. +* Send a pull request (PR) once you translate any document. Please translate documents & send PRs one by one. Don't wait to finish translations for all documents. -GitHub issues are for bug reports, feature requests and other discussions about the framework. +### Resource Localization -If you're creating a bug/problem report, please include followings: +ABP framework has a flexible [localization system](https://abp.io/documents/abp/latest/Localization). You can create localized user interfaces for your own application. -* Your Abp package version. -* Your base framework: .Net Framework or .Net Core. -* Exception message and stack trace if available. -* Steps needed to reproduce the problem. +In addition to that, the framework and pre-build modules have already localized texts. As an example, see [the localization texts for the Volo.Abp.UI package](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). You can create a new file in the [same folder](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) to translate it. -Please write in English. +* Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. +* Create a new file for the target language for a localization text (json) file (near to the en.json file). +* Copy all texts from the en.json file. +* Translate the texts. +* Send pull request on Github. -### Stack Overflow +ABP is a modular framework. So there are many localization text resource, one per module. To find all .json files, you can search for "en.json" after cloning the repository. You can also check [this list](Localization-Text-Files.md) for a list of localization text files. -Please use Stack Overflow for your questions about using the framework, templates and samples: +### Blog Posts & Tutorials -https://stackoverflow.com/questions/tagged/aspnetboilerplate +If you decide to create some tutorials or blog posts on ABP, please inform us (by creating a [Github issue](https://github.com/abpframework/abp/issues)), so we may add a link to your tutorial/post in the official documentation and we can announce it on our [Twitter account](https://twitter.com/abpframework). -Use aspnetboilerplate tag in your questions. +### Bug Report -``` +If you find any bug, please [create an issue on the Github repository](https://github.com/abpframework/abp/issues/new). From b2058277f978976414674b1bc09a5fee1caa569e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20=C3=96zceviz?= Date: Wed, 3 Apr 2019 13:16:37 +0200 Subject: [PATCH 037/116] Migration method changed to reduce the size of the image --- templates/service/database/Dockerfile | 15 +++++++-------- templates/service/database/entrypoint.sh | 12 ++++++------ templates/service/docker-compose.migrations.yml | 9 +++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/templates/service/database/Dockerfile b/templates/service/database/Dockerfile index 74bbe2c820..c5e3bcb0c1 100644 --- a/templates/service/database/Dockerfile +++ b/templates/service/database/Dockerfile @@ -1,19 +1,18 @@ FROM microsoft/dotnet:2.2-sdk-alpine AS build -RUN apk add --no-cache bash COPY . . WORKDIR /templates/service/host/IdentityServerHost -RUN dotnet restore -nowarn:msb3202,nu1503 -RUN dotnet build --no-restore -c Release +RUN dotnet restore +RUN dotnet ef migrations script -i -o migrations-IdentityServerHost.sql WORKDIR /templates/service/host/MyCompanyName.MyProjectName.Host -RUN dotnet restore -nowarn:msb3202,nu1503 -RUN dotnet build --no-restore -c Release +RUN dotnet restore +RUN dotnet ef migrations script -i -o migrations-MyProjectName.sql -FROM build AS final +FROM mcr.microsoft.com/mssql-tools AS final WORKDIR /src -COPY --from=build /templates/service/host/IdentityServerHost ./IdentityServerHost -COPY --from=build /templates/service/host/MyCompanyName.MyProjectName.Host ./MyCompanyName.MyProjectName.Host +COPY --from=build /templates/service/host/IdentityServerHost/migrations-IdentityServerHost.sql migrations-IdentityServerHost.sql +COPY --from=build /templates/service/host/MyCompanyName.MyProjectName.Host/migrations-MyProjectName.sql migrations-MyProjectName.sql COPY --from=build /templates/service/database/entrypoint.sh . RUN /bin/bash -c "sed -i $'s/\r$//' entrypoint.sh" RUN chmod +x ./entrypoint.sh diff --git a/templates/service/database/entrypoint.sh b/templates/service/database/entrypoint.sh index 80f1b43dbc..353c7e1dc0 100644 --- a/templates/service/database/entrypoint.sh +++ b/templates/service/database/entrypoint.sh @@ -1,12 +1,12 @@ #!/bin/bash -cd IdentityServerHost -export ConnectionStrings__Default=$IdentityServerConnectionString - -until dotnet ef database update --no-build; do +until /opt/mssql-tools/bin/sqlcmd -S sqlserver -U SA -P $SA_PASSWORD -Q 'SELECT name FROM master.sys.databases'; do >&2 echo "SQL Server is starting up" sleep 1 done -export ConnectionStrings__Default=$MyProjectNameConnectionString -cd MyCompanyName.MyProjectName.Host && dotnet ef database update --no-build \ No newline at end of file +/opt/mssql-tools/bin/sqlcmd -S sqlserver -U SA -P $SA_PASSWORD -Q "CREATE DATABASE [$IdentityServer_DB]" +/opt/mssql-tools/bin/sqlcmd -S sqlserver -U SA -P $SA_PASSWORD -Q "CREATE DATABASE [$MyProjectName_DB]" + +/opt/mssql-tools/bin/sqlcmd -d $IdentityServer_DB -S sqlserver -U sa -P $SA_PASSWORD -i migrations-IdentityServerHost.sql +/opt/mssql-tools/bin/sqlcmd -d $MyProjectName_DB -S sqlserver -U sa -P $SA_PASSWORD -i migrations-MyProjectName.sql \ No newline at end of file diff --git a/templates/service/docker-compose.migrations.yml b/templates/service/docker-compose.migrations.yml index 33d32f8faf..e2937e32c6 100644 --- a/templates/service/docker-compose.migrations.yml +++ b/templates/service/docker-compose.migrations.yml @@ -1,12 +1,13 @@ version: '3.4' -services: +services: migrations: build: context: ../../ - dockerfile: templates/service/database/Dockerfile + dockerfile: templates/service/database/Dockerfile depends_on: - sqlserver environment: - - IdentityServerConnectionString=Server=sqlserver;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - - MyProjectNameConnectionString=Server=sqlserver;Database=MyProjectName_ModuleDb;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false + - IdentityServer_DB=MyProjectName_Identity + - MyProjectName_DB=MyProjectName_ModuleDb + - SA_PASSWORD=yourStrong(!)Password From 0984d2d449d04fe7e28acb1a1a5129cbd7423458 Mon Sep 17 00:00:00 2001 From: Lanpin Date: Wed, 3 Apr 2019 21:00:14 +0800 Subject: [PATCH 038/116] fixed PublicProductsController namespace --- .../ProductManagement/PublicProductsController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.HttpApi/ProductManagement/PublicProductsController.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.HttpApi/ProductManagement/PublicProductsController.cs index 3ba14828ad..c87ee729ac 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.HttpApi/ProductManagement/PublicProductsController.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.HttpApi/ProductManagement/PublicProductsController.cs @@ -5,7 +5,7 @@ using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.AspNetCore.Mvc; -namespace MyCompanyName.ProductManagement +namespace ProductManagement { [RemoteService] [Area("productManagement")] From 2de21f72d609a266e36e6fc7ef62381a69280cf1 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Wed, 3 Apr 2019 16:36:32 +0300 Subject: [PATCH 039/116] Inherit aggregate roots from FullAuditedAggregateRoot --- .../Volo/Abp/IdentityServer/ApiResources/ApiResource.cs | 4 ++-- .../Volo/Abp/IdentityServer/Clients/Client.cs | 5 ++--- .../Abp/IdentityServer/IdentityResources/IdentityResource.cs | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResource.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResource.cs index c6c74ac24f..12865c653d 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResource.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResource.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.Linq; using IdentityServer4; using JetBrains.Annotations; -using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Entities.Auditing; namespace Volo.Abp.IdentityServer.ApiResources { - public class ApiResource : AggregateRoot + public class ApiResource : FullAuditedAggregateRoot { [NotNull] public virtual string Name { get; protected set; } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/Client.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/Client.cs index f45926fb9a..35aaa20652 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/Client.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/Client.cs @@ -4,12 +4,11 @@ using System.Linq; using IdentityServer4; using IdentityServer4.Models; using JetBrains.Annotations; -using Volo.Abp.Domain.Entities; -using Volo.Abp.Guids; +using Volo.Abp.Domain.Entities.Auditing; namespace Volo.Abp.IdentityServer.Clients { - public class Client : AggregateRoot + public class Client : FullAuditedAggregateRoot { public virtual string ClientId { get; set; } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IdentityResource.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IdentityResource.cs index 67c8e37a65..574f3aa322 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IdentityResource.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IdentityResource.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; -using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Entities.Auditing; namespace Volo.Abp.IdentityServer.IdentityResources { - public class IdentityResource : AggregateRoot + public class IdentityResource : FullAuditedAggregateRoot { public virtual string Name { get; set; } From 8a7aebaebcdb4b355677899840f26ce332d29246 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Wed, 3 Apr 2019 16:58:27 +0300 Subject: [PATCH 040/116] Run domain layer tests on EF core integration. --- .../AllowedCorsOriginsCacheItemInvalidator.cs | 10 +++++++++- .../Volo.Abp.IdentityServer.Domain.Tests.csproj | 3 +-- .../AbpIdentityServerDomainTestModule.cs | 2 +- .../Abp/IdentityServer/CorsPolicyService_Tests.cs | 14 +++++++++++--- .../AbpIdentityServerTestDataBuilder.cs | 2 ++ ...pIdentityServerTestEntityFrameworkCoreModule.cs | 2 -- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AllowedCorsOriginsCacheItemInvalidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AllowedCorsOriginsCacheItemInvalidator.cs index 92583dc4df..80bc425d5c 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AllowedCorsOriginsCacheItemInvalidator.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AllowedCorsOriginsCacheItemInvalidator.cs @@ -7,7 +7,10 @@ using Volo.Abp.IdentityServer.Clients; namespace Volo.Abp.IdentityServer { - public class AllowedCorsOriginsCacheItemInvalidator : ILocalEventHandler>, ITransientDependency + public class AllowedCorsOriginsCacheItemInvalidator : + ILocalEventHandler>, + ILocalEventHandler>, + ITransientDependency { protected IDistributedCache Cache { get; } @@ -20,5 +23,10 @@ namespace Volo.Abp.IdentityServer { await Cache.RemoveAsync(AllowedCorsOriginsCacheItem.AllOrigins); } + + public async Task HandleEventAsync(EntityChangedEventData eventData) + { + await Cache.RemoveAsync(AllowedCorsOriginsCacheItem.AllOrigins); + } } } \ No newline at end of file diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo.Abp.IdentityServer.Domain.Tests.csproj b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo.Abp.IdentityServer.Domain.Tests.csproj index d5bad1ad08..ab9f99a791 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo.Abp.IdentityServer.Domain.Tests.csproj +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo.Abp.IdentityServer.Domain.Tests.csproj @@ -12,8 +12,7 @@ - - + diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpIdentityServerDomainTestModule.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpIdentityServerDomainTestModule.cs index fd7736f9cf..e219427d03 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpIdentityServerDomainTestModule.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpIdentityServerDomainTestModule.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.IdentityServer { - [DependsOn(typeof(AbpIdentityServerMongoDbTestModule))] + [DependsOn(typeof(AbpIdentityServerTestEntityFrameworkCoreModule))] public class AbpIdentityServerDomainTestModule : AbpModule { diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/CorsPolicyService_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/CorsPolicyService_Tests.cs index 0aa1e835ac..af701cfc69 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/CorsPolicyService_Tests.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/CorsPolicyService_Tests.cs @@ -2,6 +2,7 @@ using IdentityServer4.Services; using Shouldly; using Volo.Abp.IdentityServer.Clients; +using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.IdentityServer @@ -10,11 +11,13 @@ namespace Volo.Abp.IdentityServer { private readonly ICorsPolicyService _corsPolicyService; private readonly IClientRepository _clientRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; public CorsPolicyService_Tests() { _corsPolicyService = GetRequiredService(); _clientRepository = GetRequiredService(); + _unitOfWorkManager = GetRequiredService(); } [Fact] @@ -30,9 +33,14 @@ namespace Volo.Abp.IdentityServer //It does not exists before (await _corsPolicyService.IsOriginAllowedAsync("https://new-origin.com")).ShouldBeFalse(); - var client1 = await _clientRepository.FindByCliendIdAsync("ClientId1"); - client1.AddCorsOrigin("https://new-origin.com"); - await _clientRepository.UpdateAsync(client1); + using (var uow = _unitOfWorkManager.Begin()) + { + var client1 = await _clientRepository.FindByCliendIdAsync("ClientId1"); + client1.AddCorsOrigin("https://new-origin.com"); + await _clientRepository.UpdateAsync(client1); + + await uow.CompleteAsync(); + } //It does exists now (await _corsPolicyService.IsOriginAllowedAsync("https://new-origin.com")).ShouldBeTrue(); diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs index 1bbeb65e07..2eef9ffc57 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs @@ -12,6 +12,8 @@ using PersistedGrant = Volo.Abp.IdentityServer.Grants.PersistedGrant; namespace Volo.Abp.IdentityServer { + //TODO: There are two data builders (ses AbpIdentityServerTestDataBuilder in Volo.Abp.IdentityServer.TestBase). It should be somehow unified! + public class AbpIdentityServerTestDataBuilder : ITransientDependency { private readonly IGuidGenerator _guidGenerator; diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs index 77e20531db..b5515026de 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs @@ -1,7 +1,6 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Autofac; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.IdentityServer.EntityFrameworkCore; @@ -11,7 +10,6 @@ using Volo.Abp.Uow; namespace Volo.Abp.IdentityServer { [DependsOn( - typeof(AbpAutofacModule), typeof(AbpIdentityEntityFrameworkCoreModule), typeof(AbpIdentityServerEntityFrameworkCoreModule), typeof(AbpIdentityServerTestBaseModule) From e43603d155c8915c0f1ccd2875bb9fc578b84ea1 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Wed, 3 Apr 2019 18:05:26 +0300 Subject: [PATCH 041/116] Configure audit fields for aggregate roots. --- .../IdentityServerDbContextModelCreatingExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs index 3c38f2e2c7..e98cacd81a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs @@ -26,6 +26,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore { client.ToTable(tablePrefix + "Clients", schema); + client.ConfigureFullAudited(); client.ConfigureExtraProperties(); client.Property(x => x.ClientId).HasMaxLength(ClientConsts.ClientIdMaxLength).IsRequired(); @@ -159,6 +160,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore { identityResource.ToTable(tablePrefix + "IdentityResources", schema); + identityResource.ConfigureFullAudited(); identityResource.ConfigureExtraProperties(); identityResource.Property(x => x.Name).HasMaxLength(IdentityResourceConsts.NameMaxLength).IsRequired(); @@ -181,6 +183,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore { apiResource.ToTable(tablePrefix + "ApiResources", schema); + apiResource.ConfigureFullAudited(); apiResource.ConfigureExtraProperties(); apiResource.Property(x => x.Name).HasMaxLength(ApiResourceConsts.NameMaxLength).IsRequired(); From bd5381a18735fa8691169da9fb0987e651946af4 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Wed, 3 Apr 2019 18:05:39 +0300 Subject: [PATCH 042/116] add Volo.Abp.Identity.MongoDB dependency --- .../Volo.Abp.IdentityServer.MongoDB.Tests.csproj | 2 ++ .../Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo.Abp.IdentityServer.MongoDB.Tests.csproj b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo.Abp.IdentityServer.MongoDB.Tests.csproj index dd342d8209..1c6030753c 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo.Abp.IdentityServer.MongoDB.Tests.csproj +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo.Abp.IdentityServer.MongoDB.Tests.csproj @@ -12,6 +12,8 @@ + + diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs index b9da4bb039..53e4394b48 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs @@ -1,5 +1,6 @@ using Mongo2Go; using Volo.Abp.Data; +using Volo.Abp.Identity.MongoDB; using Volo.Abp.IdentityServer.MongoDB; using Volo.Abp.Modularity; @@ -8,7 +9,8 @@ namespace Volo.Abp.IdentityServer [DependsOn( typeof(AbpIdentityServerTestBaseModule), - typeof(AbpIdentityServerMongoDbModule) + typeof(AbpIdentityServerMongoDbModule), + typeof(AbpIdentityMongoDbModule) )] public class AbpIdentityServerMongoDbTestModule : AbpModule { From e785193c1662e5f236a4f3a148ec918403c52774 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Wed, 3 Apr 2019 18:11:15 +0300 Subject: [PATCH 043/116] add buttons to tenant create/edit modals --- .../Pages/TenantManagement/Tenants/CreateModal.cshtml | 3 ++- .../Pages/TenantManagement/Tenants/EditModal.cshtml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml index ecef85ac8c..b3a2c9d5be 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml @@ -1,5 +1,6 @@ @page @using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.TenantManagement.Localization @using Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants @model CreateModalModel @@ -13,6 +14,6 @@ - + \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml index beee12208d..3f1c9a9d0e 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml @@ -1,5 +1,6 @@ @page @using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.TenantManagement.Localization @using Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants @model EditModalModel @@ -14,6 +15,6 @@ - + \ No newline at end of file From 6fae53a985bd538d3f0d0c1d2a449cc399529993 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 4 Apr 2019 09:00:41 +0300 Subject: [PATCH 044/116] added missing localization --- framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json | 1 + .../src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json | 1 + framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json | 1 + .../src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json | 1 + 4 files changed, 4 insertions(+) diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json index 16af838025..9eb47477f4 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json @@ -24,6 +24,7 @@ "Actions": "Actions", "Delete": "Delete", "Edit": "Edit", + "Refresh": "Refresh", "ProcessingWithThreeDot": "Processing...", "LoadingWithThreeDot": "Loading...", "Welcome": "Welcome", diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json index cbdc1623c9..d08ecb4525 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json @@ -24,6 +24,7 @@ "Actions": "Ações", "Delete": "Excluir", "Edit": "Editar", + "Refresh": "Refrescar", "ProcessingWithThreeDot": "Processando...", "Login": "Entrar", "Register": "cadastrar", diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json index cd4a3c0565..d45c353878 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json @@ -24,6 +24,7 @@ "Actions": "İşlemler", "Delete": "Sil", "Edit": "Düzenle", + "Refresh": "Yenile", "ProcessingWithThreeDot": "İşleniyor...", "LoadingWithThreeDot": "Yükleniyor...", "Welcome": "Hoşgeldiniz", diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json index 82d97d4aa9..f1814efdb3 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json @@ -24,6 +24,7 @@ "Actions": "操作", "Delete": "删除", "Edit": "修改", + "Refresh": "刷新", "ProcessingWithThreeDot": "处理中...", "LoadingWithThreeDot": "加载中...", "Welcome": "欢迎", From dd0aaddff8a076e9526ba0218fd1525b8d549c6d Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 09:18:50 +0300 Subject: [PATCH 045/116] Remove unnecessary assignments. --- .../Pages/Account/IdentityServerSupportedLoginModel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs index 1aeef4f0c6..1ed1fda49f 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs @@ -35,11 +35,9 @@ namespace Volo.Abp.Account.Web.Pages.Account schemeProvider, accountOptions) { - _schemeProvider = schemeProvider; Interaction = interaction; ClientStore = clientStore; IdentityServerEvents = identityServerEvents; - _accountOptions = accountOptions.Value; } public override async Task OnGetAsync() From 3dbdc779223d28d20299bc4a68ea0ff66ef480e5 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 10:38:13 +0300 Subject: [PATCH 046/116] Add AbpMultiTenancyClaimsIdentityExtensions --- .../Principal/AbpClaimsIdentityExtensions.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 framework/src/Volo.Abp.MultiTenancy/System/Security/Principal/AbpClaimsIdentityExtensions.cs diff --git a/framework/src/Volo.Abp.MultiTenancy/System/Security/Principal/AbpClaimsIdentityExtensions.cs b/framework/src/Volo.Abp.MultiTenancy/System/Security/Principal/AbpClaimsIdentityExtensions.cs new file mode 100644 index 0000000000..5b9d2493b9 --- /dev/null +++ b/framework/src/Volo.Abp.MultiTenancy/System/Security/Principal/AbpClaimsIdentityExtensions.cs @@ -0,0 +1,25 @@ +using System.Security.Claims; +using JetBrains.Annotations; +using Volo.Abp.MultiTenancy; + +namespace System.Security.Principal +{ + public static class AbpMultiTenancyClaimsIdentityExtensions + { + public static MultiTenancySides GetMultiTenancySide([NotNull] this IIdentity identity) + { + var tenantId = identity.FindTenantId(); + return tenantId.HasValue + ? MultiTenancySides.Tenant + : MultiTenancySides.Host; + } + + public static MultiTenancySides GetMultiTenancySide([NotNull] this ClaimsPrincipal principal) + { + var tenantId = principal.FindTenantId(); + return tenantId.HasValue + ? MultiTenancySides.Tenant + : MultiTenancySides.Host; + } + } +} From 6bf2cb94d46e3c7b58cd2db1e229a310fc8fc4d2 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 10:38:31 +0300 Subject: [PATCH 047/116] PermissionChecker should check for multitenancy side. --- .../Permissions/PermissionChecker.cs | 20 ++++++++++++++++--- .../PermissionTestDataBuilder.cs | 9 +++++++++ .../TestPermissionDefinitionProvider.cs | 3 +++ .../PermissionChecker_User_Tests.cs | 16 ++++++++++++++- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs index dbc0fc147a..84dfc91053 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; +using System.Security.Principal; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; using Volo.Abp.Security.Claims; namespace Volo.Abp.Authorization.Permissions @@ -18,6 +20,8 @@ namespace Volo.Abp.Authorization.Permissions protected ICurrentPrincipalAccessor PrincipalAccessor { get; } + protected ICurrentTenant CurrentTenant { get; } + protected PermissionOptions Options { get; } private readonly Lazy> _lazyProviders; @@ -26,10 +30,12 @@ namespace Volo.Abp.Authorization.Permissions IOptions options, IServiceProvider serviceProvider, ICurrentPrincipalAccessor principalAccessor, - IPermissionDefinitionManager permissionDefinitionManager) + IPermissionDefinitionManager permissionDefinitionManager, + ICurrentTenant currentTenant) { PrincipalAccessor = principalAccessor; PermissionDefinitionManager = permissionDefinitionManager; + CurrentTenant = currentTenant; Options = options.Value; _lazyProviders = new Lazy>( @@ -50,9 +56,17 @@ namespace Volo.Abp.Authorization.Permissions { Check.NotNull(name, nameof(name)); - var isGranted = false; - var permission = PermissionDefinitionManager.Get(name); + + var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() + ?? CurrentTenant.GetMultiTenancySide(); + + if (!permission.MultiTenancySide.HasFlag(multiTenancySide)) + { + return false; + } + + var isGranted = false; var context = new PermissionValueCheckContext(permission, claimsPrincipal); foreach (var provider in ValueProviders) { diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/PermissionTestDataBuilder.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/PermissionTestDataBuilder.cs index 81fb98b2be..9d9e2ab10f 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/PermissionTestDataBuilder.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/PermissionTestDataBuilder.cs @@ -29,6 +29,15 @@ namespace Volo.Abp.PermissionManagement User1Id.ToString() ) ); + + _permissionGrantRepository.Insert( + new PermissionGrant( + _guidGenerator.Create(), + "MyPermission3", + UserPermissionValueProvider.ProviderName, + User1Id.ToString() + ) + ); } } } \ No newline at end of file diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs index bd997f76d4..5dfc6431c4 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs @@ -1,4 +1,5 @@ using Volo.Abp.Authorization.Permissions; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.PermissionManagement { @@ -12,6 +13,8 @@ namespace Volo.Abp.PermissionManagement var myPermission2 = testGroup.AddPermission("MyPermission2"); myPermission2.AddChild("MyPermission2.ChildPermission1"); + + testGroup.AddPermission("MyPermission3", multiTenancySide: MultiTenancySides.Host); } } } \ No newline at end of file diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo/Abp/PermissionManagement/PermissionChecker_User_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo/Abp/PermissionManagement/PermissionChecker_User_Tests.cs index 280cc0d638..b49ff94f90 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo/Abp/PermissionManagement/PermissionChecker_User_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo/Abp/PermissionManagement/PermissionChecker_User_Tests.cs @@ -44,7 +44,16 @@ namespace Volo.Abp.PermissionManagement )).ShouldBeFalse(); } - private static ClaimsPrincipal CreatePrincipal(Guid? userId) + [Fact] + public async Task Should_Not_Allow_Host_Permission_To_Tenant_User_Even_Granted_Before() + { + (await _permissionChecker.IsGrantedAsync( + CreatePrincipal(PermissionTestDataBuilder.User1Id, Guid.NewGuid()), + "MyPermission3" + )).ShouldBeFalse(); + } + + private static ClaimsPrincipal CreatePrincipal(Guid? userId, Guid? tenantId = null) { var claimsIdentity = new ClaimsIdentity(); @@ -53,6 +62,11 @@ namespace Volo.Abp.PermissionManagement claimsIdentity.AddClaim(new Claim(AbpClaimTypes.UserId, userId.ToString())); } + if (tenantId != null) + { + claimsIdentity.AddClaim(new Claim(AbpClaimTypes.TenantId, tenantId.ToString())); + } + return new ClaimsPrincipal(claimsIdentity); } } From 4285e00adbad4a0d5d97b5313af78cb15b3d579f Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 11:40:45 +0300 Subject: [PATCH 048/116] Arrange parameter formatting --- .../Volo/Abp/Caching/DistributedCache.cs | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index f65683042b..712a350108 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -57,7 +57,9 @@ namespace Volo.Abp.Caching SetDefaultOptions(); } - public virtual TCacheItem Get(string key, bool? hideErrors = null) + public virtual TCacheItem Get( + string key, + bool? hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -86,7 +88,10 @@ namespace Volo.Abp.Caching return ObjectSerializer.Deserialize(cachedBytes); } - public virtual async Task GetAsync(string key, bool? hideErrors = null, CancellationToken token = default) + public virtual async Task GetAsync( + string key, + bool? hideErrors = null, + CancellationToken token = default) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -174,7 +179,11 @@ namespace Volo.Abp.Caching return value; } - public virtual void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null) + public virtual void Set( + string key, + TCacheItem value, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -198,7 +207,12 @@ namespace Volo.Abp.Caching } } - public virtual async Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null, CancellationToken token = default) + public virtual async Task SetAsync( + string key, + TCacheItem value, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null, + CancellationToken token = default) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -223,7 +237,9 @@ namespace Volo.Abp.Caching } } - public virtual void Refresh(string key, bool? hideErrors = null) + public virtual void Refresh( + string key, + bool? hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -243,7 +259,10 @@ namespace Volo.Abp.Caching } } - public virtual async Task RefreshAsync(string key, bool? hideErrors = null, CancellationToken token = default) + public virtual async Task RefreshAsync( + string key, + bool? hideErrors = null, + CancellationToken token = default) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -263,7 +282,9 @@ namespace Volo.Abp.Caching } } - public virtual void Remove(string key, bool? hideErrors = null) + public virtual void Remove( + string key, + bool? hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -282,7 +303,10 @@ namespace Volo.Abp.Caching } } - public virtual async Task RemoveAsync(string key, bool? hideErrors = null, CancellationToken token = default) + public virtual async Task RemoveAsync( + string key, + bool? hideErrors = null, + CancellationToken token = default) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; From 575d6bda54408d80d88af27fcddd8e61e6194f86 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 15:38:45 +0300 Subject: [PATCH 049/116] Introduce IDistributedCacheSerializer and implement as Utf8JsonDistributedCacheSerializer --- .../Volo.Abp.Caching/Volo.Abp.Caching.csproj | 1 + .../Volo/Abp/Caching/AbpCachingModule.cs | 9 ++++--- .../Volo/Abp/Caching/DistributedCache.cs | 22 ++++++++-------- .../Caching/IDistributedCacheSerializer.cs | 9 +++++++ .../Utf8JsonDistributedCacheSerializer.cs | 26 +++++++++++++++++++ 5 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCacheSerializer.cs create mode 100644 framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Utf8JsonDistributedCacheSerializer.cs diff --git a/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj b/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj index e9a5acd990..f4417ae910 100644 --- a/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj +++ b/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj @@ -18,6 +18,7 @@ + diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs index 3a1537cba7..48d55cc0d5 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using System; +using Volo.Abp.Json; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.Serialization; @@ -7,9 +8,11 @@ using Volo.Abp.Threading; namespace Volo.Abp.Caching { - [DependsOn(typeof(AbpThreadingModule))] - [DependsOn(typeof(AbpSerializationModule))] - [DependsOn(typeof(AbpMultiTenancyModule))] + [DependsOn( + typeof(AbpThreadingModule), + typeof(AbpSerializationModule), + typeof(AbpMultiTenancyModule), + typeof(AbpJsonModule))] public class AbpCachingModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 712a350108..75b024740c 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -25,11 +25,12 @@ namespace Volo.Abp.Caching protected ICancellationTokenProvider CancellationTokenProvider { get; } - protected IObjectSerializer ObjectSerializer { get; } + //TODO: Create IDistributedCacheSerializer + protected IDistributedCacheSerializer Serializer { get; } protected ICurrentTenant CurrentTenant { get; } - protected AsyncLock AsyncLock { get; } = new AsyncLock(); + //protected AsyncLock AsyncLock { get; } = new AsyncLock(); protected DistributedCacheEntryOptions DefaultCacheOptions; @@ -42,8 +43,7 @@ namespace Volo.Abp.Caching IOptions distributedCacheOption, IDistributedCache cache, ICancellationTokenProvider cancellationTokenProvider, - - IObjectSerializer objectSerializer, + IDistributedCacheSerializer serializer, ICurrentTenant currentTenant) { _distributedCacheOption = distributedCacheOption.Value; @@ -51,7 +51,7 @@ namespace Volo.Abp.Caching Cache = cache; CancellationTokenProvider = cancellationTokenProvider; Logger = NullLogger>.Instance; - ObjectSerializer = objectSerializer; + Serializer = serializer; CurrentTenant = currentTenant; SetDefaultOptions(); @@ -85,7 +85,7 @@ namespace Volo.Abp.Caching return null; } - return ObjectSerializer.Deserialize(cachedBytes); + return Serializer.Deserialize(cachedBytes); } public virtual async Task GetAsync( @@ -120,7 +120,7 @@ namespace Volo.Abp.Caching return null; } - return ObjectSerializer.Deserialize(cachedBytes); + return Serializer.Deserialize(cachedBytes); } public TCacheItem GetOrAdd( @@ -135,7 +135,7 @@ namespace Volo.Abp.Caching return value; } - using (AsyncLock.Lock(CancellationTokenProvider.Token)) + //using (AsyncLock.Lock(CancellationTokenProvider.Token)) { value = Get(key, hideErrors); if (value != null) @@ -164,7 +164,7 @@ namespace Volo.Abp.Caching return value; } - using (await AsyncLock.LockAsync(token)) + //using (await AsyncLock.LockAsync(token)) { value = await GetAsync(key, hideErrors, token); if (value != null) @@ -191,7 +191,7 @@ namespace Volo.Abp.Caching { Cache.Set( NormalizeKey(key), - ObjectSerializer.Serialize(value), + Serializer.Serialize(value), options ?? DefaultCacheOptions ); } @@ -220,7 +220,7 @@ namespace Volo.Abp.Caching { await Cache.SetAsync( NormalizeKey(key), - ObjectSerializer.Serialize(value), + Serializer.Serialize(value), options ?? DefaultCacheOptions, CancellationTokenProvider.FallbackToProvider(token) ); diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCacheSerializer.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCacheSerializer.cs new file mode 100644 index 0000000000..a26620f17c --- /dev/null +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCacheSerializer.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Caching +{ + public interface IDistributedCacheSerializer + { + byte[] Serialize(T obj); + + T Deserialize(byte[] bytes); + } +} diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Utf8JsonDistributedCacheSerializer.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Utf8JsonDistributedCacheSerializer.cs new file mode 100644 index 0000000000..a99127098f --- /dev/null +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Utf8JsonDistributedCacheSerializer.cs @@ -0,0 +1,26 @@ +using System.Text; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; + +namespace Volo.Abp.Caching +{ + public class Utf8JsonDistributedCacheSerializer : IDistributedCacheSerializer, ITransientDependency + { + protected IJsonSerializer JsonSerializer { get; } + + public Utf8JsonDistributedCacheSerializer(IJsonSerializer jsonSerializer) + { + JsonSerializer = jsonSerializer; + } + + public byte[] Serialize(T obj) + { + return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(obj)); + } + + public T Deserialize(byte[] bytes) + { + return (T)JsonSerializer.Deserialize(typeof(T), Encoding.UTF8.GetString(bytes)); + } + } +} \ No newline at end of file From 9fbb3f1104d81711979ad6bc080b5786e82738b7 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 16:12:45 +0300 Subject: [PATCH 050/116] Add migration for microservice --- ...536_Make_IDS4_Entities_Audited.Designer.cs | 1227 +++++++++++++++++ ...190404130536_Make_IDS4_Entities_Audited.cs | 347 +++++ .../AuthServerDbContextModelSnapshot.cs | 87 +- 3 files changed, 1654 insertions(+), 7 deletions(-) create mode 100644 samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20190404130536_Make_IDS4_Entities_Audited.Designer.cs create mode 100644 samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20190404130536_Make_IDS4_Entities_Audited.cs diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20190404130536_Make_IDS4_Entities_Audited.Designer.cs b/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20190404130536_Make_IDS4_Entities_Audited.Designer.cs new file mode 100644 index 0000000000..4dac4a6a72 --- /dev/null +++ b/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20190404130536_Make_IDS4_Entities_Audited.Designer.cs @@ -0,0 +1,1227 @@ +// +using System; +using AuthServer.Host.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace AuthServer.Host.Migrations +{ + [DbContext(typeof(AuthServerDbContext))] + [Migration("20190404130536_Make_IDS4_Entities_Audited")] + partial class Make_IDS4_Entities_Audited + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TenantName"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTenantId"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Regex") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasMaxLength(128); + + b.Property("Required"); + + b.Property("ValueType"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasDefaultValue(false); + + b.Property("LockoutEnd"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .HasColumnName("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196); + + b.Property("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("DisplayName") + .HasMaxLength(128); + + b.Property("Emphasize"); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("ApiResourceId", "Name"); + + b.ToTable("IdentityServerApiScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Name", "Type"); + + b.ToTable("IdentityServerApiScopeClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AbsoluteRefreshTokenLifetime"); + + b.Property("AccessTokenLifetime"); + + b.Property("AccessTokenType"); + + b.Property("AllowAccessTokensViaBrowser"); + + b.Property("AllowOfflineAccess"); + + b.Property("AllowPlainTextPkce"); + + b.Property("AllowRememberConsent"); + + b.Property("AlwaysIncludeUserClaimsInIdToken"); + + b.Property("AlwaysSendClientClaims"); + + b.Property("AuthorizationCodeLifetime"); + + b.Property("BackChannelLogoutSessionRequired"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(300); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ClientName") + .HasMaxLength(200); + + b.Property("ClientUri") + .HasMaxLength(300); + + b.Property("ConcurrencyStamp"); + + b.Property("ConsentLifetime"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("EnableLocalLogin"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(300); + + b.Property("IdentityTokenLifetime"); + + b.Property("IncludeJwtId"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(300); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200); + + b.Property("RefreshTokenExpiration"); + + b.Property("RefreshTokenUsage"); + + b.Property("RequireClientSecret"); + + b.Property("RequireConsent"); + + b.Property("RequirePkce"); + + b.Property("SlidingRefreshTokenLifetime"); + + b.Property("UpdateAccessTokenClaimsOnRefresh"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("IdentityServerClients"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(250); + + b.Property("Value") + .HasMaxLength(250); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId"); + + b.Property("Origin") + .HasMaxLength(150); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId"); + + b.Property("GrantType") + .HasMaxLength(196); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId"); + + b.Property("Provider") + .HasMaxLength(64); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId"); + + b.Property("Key") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("ClientId", "Key"); + + b.ToTable("IdentityServerClientProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("RedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId"); + + b.Property("Scope") + .HasMaxLength(196); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime"); + + b.Property("Data") + .IsRequired(); + + b.Property("Expiration"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("Id"); + + b.Property("SubjectId") + .HasMaxLength(200); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50); + + b.HasKey("Key"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.ToTable("IdentityServerPersistedGrants"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.Property("IdentityResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Emphasize"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiScope") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId", "Name") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource") + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20190404130536_Make_IDS4_Entities_Audited.cs b/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20190404130536_Make_IDS4_Entities_Audited.cs new file mode 100644 index 0000000000..5ef97dc0af --- /dev/null +++ b/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/20190404130536_Make_IDS4_Entities_Audited.cs @@ -0,0 +1,347 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace AuthServer.Host.Migrations +{ + public partial class Make_IDS4_Entities_Audited : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CreationTime", + table: "IdentityServerIdentityResources", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "CreatorId", + table: "IdentityServerIdentityResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeleterId", + table: "IdentityServerIdentityResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeletionTime", + table: "IdentityServerIdentityResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "IdentityServerIdentityResources", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "LastModificationTime", + table: "IdentityServerIdentityResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "LastModifierId", + table: "IdentityServerIdentityResources", + nullable: true); + + migrationBuilder.AlterColumn( + name: "LogoUri", + table: "IdentityServerClients", + maxLength: 300, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 2000, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "FrontChannelLogoutUri", + table: "IdentityServerClients", + maxLength: 300, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 2000, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ClientUri", + table: "IdentityServerClients", + maxLength: 300, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 2000, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "BackChannelLogoutUri", + table: "IdentityServerClients", + maxLength: 300, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 2000, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "CreationTime", + table: "IdentityServerClients", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "CreatorId", + table: "IdentityServerClients", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeleterId", + table: "IdentityServerClients", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeletionTime", + table: "IdentityServerClients", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "IdentityServerClients", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "LastModificationTime", + table: "IdentityServerClients", + nullable: true); + + migrationBuilder.AddColumn( + name: "LastModifierId", + table: "IdentityServerClients", + nullable: true); + + //migrationBuilder.AlterColumn( + // name: "RedirectUri", + // table: "IdentityServerClientRedirectUris", + // maxLength: 200, + // nullable: false, + // oldClrType: typeof(string), + // oldMaxLength: 2000); + + //migrationBuilder.AlterColumn( + // name: "Value", + // table: "IdentityServerClientProperties", + // maxLength: 128, + // nullable: false, + // oldClrType: typeof(string), + // oldMaxLength: 2000); + + //migrationBuilder.AlterColumn( + // name: "Key", + // table: "IdentityServerClientProperties", + // maxLength: 64, + // nullable: false, + // oldClrType: typeof(string), + // oldMaxLength: 250); + + migrationBuilder.AddColumn( + name: "CreationTime", + table: "IdentityServerApiResources", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "CreatorId", + table: "IdentityServerApiResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeleterId", + table: "IdentityServerApiResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeletionTime", + table: "IdentityServerApiResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "IdentityServerApiResources", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "LastModificationTime", + table: "IdentityServerApiResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "LastModifierId", + table: "IdentityServerApiResources", + nullable: true); + + migrationBuilder.AddColumn( + name: "EntityTenantId", + table: "AbpEntityChanges", + nullable: true); + + migrationBuilder.AddColumn( + name: "TenantName", + table: "AbpAuditLogs", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CreationTime", + table: "IdentityServerIdentityResources"); + + migrationBuilder.DropColumn( + name: "CreatorId", + table: "IdentityServerIdentityResources"); + + migrationBuilder.DropColumn( + name: "DeleterId", + table: "IdentityServerIdentityResources"); + + migrationBuilder.DropColumn( + name: "DeletionTime", + table: "IdentityServerIdentityResources"); + + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "IdentityServerIdentityResources"); + + migrationBuilder.DropColumn( + name: "LastModificationTime", + table: "IdentityServerIdentityResources"); + + migrationBuilder.DropColumn( + name: "LastModifierId", + table: "IdentityServerIdentityResources"); + + migrationBuilder.DropColumn( + name: "CreationTime", + table: "IdentityServerClients"); + + migrationBuilder.DropColumn( + name: "CreatorId", + table: "IdentityServerClients"); + + migrationBuilder.DropColumn( + name: "DeleterId", + table: "IdentityServerClients"); + + migrationBuilder.DropColumn( + name: "DeletionTime", + table: "IdentityServerClients"); + + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "IdentityServerClients"); + + migrationBuilder.DropColumn( + name: "LastModificationTime", + table: "IdentityServerClients"); + + migrationBuilder.DropColumn( + name: "LastModifierId", + table: "IdentityServerClients"); + + migrationBuilder.DropColumn( + name: "CreationTime", + table: "IdentityServerApiResources"); + + migrationBuilder.DropColumn( + name: "CreatorId", + table: "IdentityServerApiResources"); + + migrationBuilder.DropColumn( + name: "DeleterId", + table: "IdentityServerApiResources"); + + migrationBuilder.DropColumn( + name: "DeletionTime", + table: "IdentityServerApiResources"); + + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "IdentityServerApiResources"); + + migrationBuilder.DropColumn( + name: "LastModificationTime", + table: "IdentityServerApiResources"); + + migrationBuilder.DropColumn( + name: "LastModifierId", + table: "IdentityServerApiResources"); + + migrationBuilder.DropColumn( + name: "EntityTenantId", + table: "AbpEntityChanges"); + + migrationBuilder.DropColumn( + name: "TenantName", + table: "AbpAuditLogs"); + + migrationBuilder.AlterColumn( + name: "LogoUri", + table: "IdentityServerClients", + maxLength: 2000, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 300, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "FrontChannelLogoutUri", + table: "IdentityServerClients", + maxLength: 2000, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 300, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ClientUri", + table: "IdentityServerClients", + maxLength: 2000, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 300, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "BackChannelLogoutUri", + table: "IdentityServerClients", + maxLength: 2000, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 300, + oldNullable: true); + + //migrationBuilder.AlterColumn( + // name: "RedirectUri", + // table: "IdentityServerClientRedirectUris", + // maxLength: 2000, + // nullable: false, + // oldClrType: typeof(string), + // oldMaxLength: 200); + + //migrationBuilder.AlterColumn( + // name: "Value", + // table: "IdentityServerClientProperties", + // maxLength: 2000, + // nullable: false, + // oldClrType: typeof(string), + // oldMaxLength: 128); + + //migrationBuilder.AlterColumn( + // name: "Key", + // table: "IdentityServerClientProperties", + // maxLength: 250, + // nullable: false, + // oldClrType: typeof(string), + // oldMaxLength: 64); + } + } +} diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/AuthServerDbContextModelSnapshot.cs b/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/AuthServerDbContextModelSnapshot.cs index ee3cf09fd3..361a93da3b 100644 --- a/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/AuthServerDbContextModelSnapshot.cs +++ b/samples/MicroserviceDemo/applications/AuthServer.Host/Migrations/AuthServerDbContextModelSnapshot.cs @@ -82,6 +82,8 @@ namespace AuthServer.Host.Migrations b.Property("TenantId") .HasColumnName("TenantId"); + b.Property("TenantName"); + b.Property("Url") .HasColumnName("Url") .HasMaxLength(256); @@ -161,6 +163,8 @@ namespace AuthServer.Host.Migrations .HasColumnName("EntityId") .HasMaxLength(128); + b.Property("EntityTenantId"); + b.Property("EntityTypeFullName") .IsRequired() .HasColumnName("EntityTypeFullName") @@ -520,6 +524,18 @@ namespace AuthServer.Host.Migrations b.Property("ConcurrencyStamp"); + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + b.Property("Description") .HasMaxLength(1000); @@ -531,6 +547,17 @@ namespace AuthServer.Host.Migrations b.Property("ExtraProperties") .HasColumnName("ExtraProperties"); + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + b.Property("Name") .IsRequired() .HasMaxLength(200); @@ -639,7 +666,7 @@ namespace AuthServer.Host.Migrations b.Property("BackChannelLogoutSessionRequired"); b.Property("BackChannelLogoutUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("ClientClaimsPrefix") .HasMaxLength(200); @@ -652,12 +679,24 @@ namespace AuthServer.Host.Migrations .HasMaxLength(200); b.Property("ClientUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("ConcurrencyStamp"); b.Property("ConsentLifetime"); + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + b.Property("Description") .HasMaxLength(1000); @@ -671,14 +710,25 @@ namespace AuthServer.Host.Migrations b.Property("FrontChannelLogoutSessionRequired"); b.Property("FrontChannelLogoutUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("IdentityTokenLifetime"); b.Property("IncludeJwtId"); + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + b.Property("LogoUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("PairWiseSubjectSalt") .HasMaxLength(200); @@ -777,11 +827,11 @@ namespace AuthServer.Host.Migrations b.Property("ClientId"); b.Property("Key") - .HasMaxLength(250); + .HasMaxLength(64); b.Property("Value") .IsRequired() - .HasMaxLength(2000); + .HasMaxLength(128); b.HasKey("ClientId", "Key"); @@ -793,7 +843,7 @@ namespace AuthServer.Host.Migrations b.Property("ClientId"); b.Property("RedirectUri") - .HasMaxLength(2000); + .HasMaxLength(200); b.HasKey("ClientId", "RedirectUri"); @@ -888,6 +938,18 @@ namespace AuthServer.Host.Migrations b.Property("ConcurrencyStamp"); + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + b.Property("Description") .HasMaxLength(1000); @@ -901,6 +963,17 @@ namespace AuthServer.Host.Migrations b.Property("ExtraProperties") .HasColumnName("ExtraProperties"); + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + b.Property("Name") .IsRequired() .HasMaxLength(200); From 43f5c80d4f1261e47f328d220f5c4f2883421189 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 17:21:52 +0300 Subject: [PATCH 051/116] Configure authentication inside AbpAccountWebIdentityServerModule to properly set default schema. --- .../AbpAccountWebIdentityServerModule.cs | 22 ++++++++++++++++++- .../AbpAccountWebModule.cs | 2 -- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs index 6a68ebc91a..b7f1557ed9 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs @@ -1,4 +1,7 @@ -using Volo.Abp.IdentityServer; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Identity.AspNetCore; +using Volo.Abp.IdentityServer; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -10,12 +13,29 @@ namespace Volo.Abp.Account.Web )] public class AbpAccountWebIdentityServerModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.PreConfigure(options => + { + options.ConfigureAuthentication = false; + }); + } + public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => { options.FileSets.AddEmbedded("Volo.Abp.Account.Web"); }); + + //TODO: Try to reuse from AbpIdentityAspNetCoreModule + context.Services + .AddAuthentication(o => + { + o.DefaultScheme = IdentityConstants.ApplicationScheme; + o.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }) + .AddIdentityCookies(); } } } diff --git a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs index e999cf1450..846c31763c 100644 --- a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs +++ b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs @@ -1,7 +1,6 @@ using Localization.Resources.AbpUi; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Account.Web.Localization; -using Volo.Abp.Account.Web.Settings; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars; @@ -9,7 +8,6 @@ using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Localization; using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Modularity; -using Volo.Abp.Settings; using Volo.Abp.UI.Navigation; using Volo.Abp.VirtualFileSystem; From 60987d14c20a49bbc2e22876a19552dd2e90afd8 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 4 Apr 2019 22:50:39 +0800 Subject: [PATCH 052/116] Add Volo.Abp.FeatureManagement unit tests. --- .../FeatureManagement/FeatureAppService.cs | 2 +- .../FeatureAppService_Tests.cs | 80 +++++++++++++++ .../FeatureManagementApplicationTestBase.cs | 11 +++ ...FeatureManagementApplicationTestModule.cs} | 2 +- .../FeatureValueCacheItemInvalidator_Tests.cs | 47 +++++++++ ...ManagementEntityFrameworkCoreTestModule.cs | 2 +- .../FeatureManagementStore_Tests.cs | 11 +++ .../AbpFeatureManagementMongoDbTestModule.cs | 2 +- .../MongoDB/FeatureManagementStore_Tests.cs | 11 +++ .../FeatureManagementStore_Tests.cs | 98 +++++++++++++++++++ ....cs => FeatureManagementTestBaseModule.cs} | 12 ++- .../FeatureManagementTestData.cs | 5 +- 12 files changed, 276 insertions(+), 7 deletions(-) create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestBase.cs rename modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/{AbpFeatureManagementApplicationTestModule.cs => FeatureManagementApplicationTestModule.cs} (75%) create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator_Tests.cs create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementStore_Tests.cs create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs rename modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/{AbpFeatureManagementTestBaseModule.cs => FeatureManagementTestBaseModule.cs} (66%) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs index b8b6c33d7c..ca2ea995c7 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs @@ -45,7 +45,7 @@ namespace Volo.Abp.FeatureManagement { Name = featureDefinition.Name, ValueType = featureDefinition.ValueType, - Description = featureDefinition.Description.Localize(_stringLocalizerFactory), + Description = featureDefinition.Description?.Localize(_stringLocalizerFactory), ParentName = featureDefinition.Parent?.Name, Value = await _featureManager.GetOrNullAsync(featureDefinition.Name, providerName, providerKey) }); diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs new file mode 100644 index 0000000000..5198ea7277 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Shouldly; +using Volo.Abp.Features; +using Volo.Abp.Users; +using Xunit; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureAppService_Tests : FeatureManagementApplicationTestBase + { + private readonly IFeatureAppService _featureAppService; + private readonly IFeatureValueRepository _featureValueRepository; + private ICurrentUser _currentUser; + private readonly FeatureManagementTestData _testData; + + + public FeatureAppService_Tests() + { + _featureAppService = GetRequiredService(); + _featureValueRepository = GetRequiredService(); + _testData = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + _currentUser = Substitute.For(); + services.AddSingleton(_currentUser); + } + + [Fact] + public async Task GetAsync() + { + Login(_testData.User1Id); + + var featureList = await _featureAppService.GetAsync(EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N")); + + featureList.ShouldNotBeNull(); + featureList.Features.ShouldContain(feature => feature.Name == TestFeatureDefinitionProvider.SocialLogins); + } + + [Fact] + public async Task UpdateAsync() + { + Login(_testData.User1Id); + + await _featureAppService.UpdateAsync(EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"), new UpdateFeaturesDto() + { + Features = new List() + { + new UpdateFeatureDto() + { + Name = TestFeatureDefinitionProvider.SocialLogins, + Value = false.ToString().ToLowerInvariant() + } + } + }); + + (await _featureAppService.GetAsync(EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).Features.Any(x => + x.Name == TestFeatureDefinitionProvider.SocialLogins && + x.Value == false.ToString().ToLowerInvariant()) + .ShouldBeTrue(); + + } + + private void Login(Guid userId) + { + _currentUser.Id.Returns(userId); + _currentUser.IsAuthenticated.Returns(true); + } + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestBase.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestBase.cs new file mode 100644 index 0000000000..f9e74aed57 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestBase.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureManagementApplicationTestBase : FeatureManagementTestBase + { + + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestModule.cs similarity index 75% rename from modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationTestModule.cs rename to modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestModule.cs index fb65739dca..2952c7da60 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationTestModule.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestModule.cs @@ -6,7 +6,7 @@ namespace Volo.Abp.FeatureManagement typeof(AbpFeatureManagementApplicationModule), typeof(AbpFeatureManagementDomainTestModule) )] - public class AbpFeatureManagementApplicationTestModule : AbpModule + public class FeatureManagementApplicationTestModule : AbpModule { } diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator_Tests.cs new file mode 100644 index 0000000000..d6666c6c09 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator_Tests.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Caching; +using Volo.Abp.Features; +using Xunit; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureValueCacheItemInvalidator_Tests : FeatureManagementTestBase + { + private IDistributedCache _cache; + private IFeatureValueRepository _featureValueRepository; + private IFeatureManagementStore _featureManagementStore; + + public FeatureValueCacheItemInvalidator_Tests() + { + _cache = GetRequiredService>(); + _featureValueRepository = GetRequiredService(); + _featureManagementStore = GetRequiredService(); + } + + [Fact] + public async Task Cache_Should_Invalidator_WhenFeatureChanged() + { + // Arrange cache feature. + await _featureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N")); + + var feature = await _featureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N")); + + // Act + await _featureValueRepository.DeleteAsync(feature); + + // Assert + (await _cache.GetAsync(FeatureValueCacheItem.CalculateCacheKey(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N")))).ShouldBeNull(); + + } + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs index 7a1eef410e..d5c573a360 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs @@ -8,7 +8,7 @@ using Volo.Abp.Modularity; namespace Volo.Abp.FeatureManagement.EntityFrameworkCore { [DependsOn( - typeof(AbpFeatureManagementTestBaseModule), + typeof(FeatureManagementTestBaseModule), typeof(AbpFeatureManagementEntityFrameworkCoreModule) )] public class AbpFeatureManagementEntityFrameworkCoreTestModule : AbpModule diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementStore_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementStore_Tests.cs new file mode 100644 index 0000000000..78f27fa222 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementStore_Tests.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.FeatureManagement.EntityFrameworkCore +{ + public class FeatureManagementStore_Tests : FeatureManagementStore_Tests + { + + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs index bc5dfa0b52..6c39cf74ae 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs @@ -5,7 +5,7 @@ using Volo.Abp.Modularity; namespace Volo.Abp.FeatureManagement.MongoDB { [DependsOn( - typeof(AbpFeatureManagementTestBaseModule), + typeof(FeatureManagementTestBaseModule), typeof(AbpFeatureManagementMongoDbModule) )] public class AbpFeatureManagementMongoDbTestModule : AbpModule diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs new file mode 100644 index 0000000000..7df6e3516b --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + public class FeatureManagementStore_Tests : FeatureManagementStore_Tests + { + + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs new file mode 100644 index 0000000000..c03c5dea65 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Features; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.FeatureManagement +{ + public abstract class FeatureManagementStore_Tests : FeatureManagementTestBase + where TStartupModule : IAbpModule + { + private IFeatureManagementStore FeatureManagementStore { get; set; } + private IFeatureValueRepository FeatureValueRepository { get; set; } + + protected FeatureManagementStore_Tests() + { + FeatureManagementStore = GetRequiredService(); + FeatureValueRepository = GetRequiredService(); + } + + [Fact] + public async Task GetOrNullAsync() + { + // Act + (await FeatureManagementStore.GetOrNullAsync(Guid.NewGuid().ToString("N"), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).ShouldBeNull(); + + (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).ShouldNotBeNull(); + } + + [Fact] + public async Task Should_Get_Null_Where_Feature_Deleted() + { + // Arrange + (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).ShouldNotBeNull(); + + // Act + await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N")); + + // Assert + (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).ShouldBeNull(); + } + + [Fact] + public async Task SetAsync() + { + // Arrange + (await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).Value.ShouldBe(true.ToString().ToLowerInvariant()); + + // Act + await FeatureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins, + false.ToString().ToUpperInvariant(), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N")); + + // Assert + (await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).Value.ShouldBe(false.ToString().ToUpperInvariant()); + } + + [Fact] + public async Task DeleteAsync() + { + // Arrange + (await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).ShouldNotBeNull(); + + // Act + await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N")); + + + // Assert + (await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N"))).ShouldBeNull(); + + + } + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/AbpFeatureManagementTestBaseModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs similarity index 66% rename from modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/AbpFeatureManagementTestBaseModule.cs rename to modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs index bf6169b488..48a90400c8 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/AbpFeatureManagementTestBaseModule.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Authorization; using Volo.Abp.Autofac; +using Volo.Abp.Features; using Volo.Abp.Modularity; namespace Volo.Abp.FeatureManagement @@ -11,7 +12,7 @@ namespace Volo.Abp.FeatureManagement typeof(AbpAuthorizationModule), typeof(AbpFeatureManagementDomainModule) )] - public class AbpFeatureManagementTestBaseModule : AbpModule + public class FeatureManagementTestBaseModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { @@ -23,6 +24,15 @@ namespace Volo.Abp.FeatureManagement SeedTestData(context); } + public override void PostConfigureServices(ServiceConfigurationContext context) + { + context.Services.Configure(options => + { + //TODO: Any value can pass. After completing the permission unit test, look at it again. + options.ProviderPolicies[EditionFeatureValueProvider.ProviderName] = EditionFeatureValueProvider.ProviderName; + }); + } + private static void SeedTestData(ApplicationInitializationContext context) { using (var scope = context.ServiceProvider.CreateScope()) diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs index e6d2273307..ede4af5f2a 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs @@ -1,9 +1,10 @@ -using Volo.Abp.DependencyInjection; +using System; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.FeatureManagement { public class FeatureManagementTestData : ISingletonDependency { - + public Guid User1Id { get; } = Guid.NewGuid(); } } From 7267e7898e3f95ca3487b47ff26cd9703f5751be Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 18:20:07 +0300 Subject: [PATCH 053/116] Resolved #962: Upgrade to Nito.AsyncEx libraries to 5.0 --- framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj b/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj index e119d294cc..71d48038e2 100644 --- a/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj +++ b/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj @@ -24,7 +24,7 @@ - - + + \ No newline at end of file From 2f54aafc94496f6ea86e59cbeea5601a0b162b6f Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 18:27:42 +0300 Subject: [PATCH 054/116] Use await RenderSectionAsync instead of RenderSection --- .../Themes/Basic/Layouts/Account.cshtml | 4 ++-- .../Themes/Basic/Layouts/Application.cshtml | 4 ++-- .../Themes/Basic/Layouts/Empty.cshtml | 4 ++-- .../Pages/_Layout.cshtml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml index 1a305434db..43c05559f9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml @@ -33,7 +33,7 @@ - @RenderSection("styles", false) + @await RenderSectionAsync("styles", false) @@ -71,7 +71,7 @@ - @RenderSection("scripts", false) + @await RenderSectionAsync("scripts", false) \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml index 30abd06404..4864f2dafe 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml @@ -23,7 +23,7 @@ - @RenderSection("styles", false) + @await RenderSectionAsync("styles", false) @@ -39,7 +39,7 @@ - @RenderSection("scripts", false) + @await RenderSectionAsync("scripts", false) \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml index c218f2a918..3b519fd223 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml @@ -22,7 +22,7 @@ - @RenderSection("styles", false) + @await RenderSectionAsync("styles", false) @@ -36,7 +36,7 @@ - @RenderSection("scripts", false) + @await RenderSectionAsync("scripts", false) \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/_Layout.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/_Layout.cshtml index c1fd7bd82b..9df9f9278f 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/_Layout.cshtml +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/_Layout.cshtml @@ -20,7 +20,7 @@ - @RenderSection("styles", false) + @await RenderSectionAsync("styles", false) @@ -30,7 +30,7 @@ - @RenderSection("scripts", false) + @await RenderSectionAsync("scripts", false) \ No newline at end of file From bf17712ae86aa502e3131525ab45695d8de1defa Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 4 Apr 2019 18:32:18 +0300 Subject: [PATCH 055/116] Use RenderSectionAsync instead of RenderSection --- .../Volo.AbpWebSite.Web/Pages/Shared/HomePageLayout.cshtml | 4 ++-- abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/Layout.cshtml | 4 ++-- .../src/Volo.AbpWebSite.Web/Pages/Shared/LayoutEmpty.cshtml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/HomePageLayout.cshtml b/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/HomePageLayout.cshtml index c0b7a79b7c..a4c25f8a7b 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/HomePageLayout.cshtml +++ b/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/HomePageLayout.cshtml @@ -17,7 +17,7 @@ @(ViewBag.Title == null ? "abp.io" : ViewBag.Title) @await Component.InvokeAsync(typeof(StandardMetaViewComponent)) - @RenderSection("styles", false) + @await RenderSectionAsync("styles", false) @@ -47,7 +47,7 @@ gtag('config', 'UA-49982725-4'); - @RenderSection("scripts", false) + @await RenderSectionAsync("scripts", false) \ No newline at end of file diff --git a/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/Layout.cshtml b/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/Layout.cshtml index 4fb93a6955..064008d0d6 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/Layout.cshtml +++ b/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/Layout.cshtml @@ -17,7 +17,7 @@ @(ViewBag.Title == null ? "abp.io" : ViewBag.Title) @await Component.InvokeAsync(typeof(StandardMetaViewComponent)) - @RenderSection("styles", false) + @await RenderSectionAsync("styles", false) @@ -58,7 +58,7 @@ gtag('config', 'UA-49982725-4'); - @RenderSection("scripts", false) + @await RenderSectionAsync("scripts", false) \ No newline at end of file diff --git a/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/LayoutEmpty.cshtml b/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/LayoutEmpty.cshtml index 99ee20dacf..f3769b34aa 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/LayoutEmpty.cshtml +++ b/abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/LayoutEmpty.cshtml @@ -18,7 +18,7 @@ @(ViewBag.Title == null ? "abp.io" : ViewBag.Title) @await Component.InvokeAsync(typeof(StandardMetaViewComponent)) - @RenderSection("styles", false) + @await RenderSectionAsync("styles", false) @@ -41,7 +41,7 @@ gtag('config', 'UA-49982725-4'); - @RenderSection("scripts", false) + @await RenderSectionAsync("scripts", false) \ No newline at end of file From eb4183ce2df3eff3000ec0d2915ebec066bdf53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enes=20Sad=C4=B1k=20=C3=96zbek?= Date: Fri, 5 Apr 2019 03:10:11 +0300 Subject: [PATCH 056/116] Fix inconsistent generic constraints in AsyncCrudAppService See #964 --- .../Volo/Abp/Application/Services/AsyncCrudAppService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs index 0a86870bb2..36c4780672 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs @@ -34,10 +34,8 @@ namespace Volo.Abp.Application.Services public abstract class AsyncCrudAppService : AsyncCrudAppService - where TGetAllInput : IPagedAndSortedResultRequest where TEntity : class, IEntity where TEntityDto : IEntityDto - where TCreateInput : IEntityDto { protected AsyncCrudAppService(IRepository repository) : base(repository) From 277684997d930dc62355b299284bf56bf309dcc1 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 5 Apr 2019 10:29:52 +0800 Subject: [PATCH 057/116] add Volo.Abp.AuditLogging unit tests. --- .../AuditLogRepository_Tests.cs | 11 + .../MongoDB/AuditLogRepository_Tests.cs | 11 + .../AuditLogging/AuditLogRepository_Tests.cs | 232 ++++++++++++++++++ .../AuditLogging_Repository_Resolve_Tests.cs | 13 + 4 files changed, 267 insertions(+) create mode 100644 modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AuditLogRepository_Tests.cs create mode 100644 modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs create mode 100644 modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs create mode 100644 modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogging_Repository_Resolve_Tests.cs diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AuditLogRepository_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AuditLogRepository_Tests.cs new file mode 100644 index 0000000000..4f626c3636 --- /dev/null +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AuditLogRepository_Tests.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.AuditLogging.EntityFrameworkCore +{ + public class AuditLogRepository_Tests : AuditLogRepository_Tests + { + + } +} diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs new file mode 100644 index 0000000000..ffcc2094ab --- /dev/null +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.AuditLogging.MongoDB +{ + public class AuditLogRepository_Tests : AuditLogRepository_Tests + { + + } +} diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs new file mode 100644 index 0000000000..27ab36f48e --- /dev/null +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Auditing; +using Volo.Abp.Guids; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.AuditLogging +{ + public abstract class AuditLogRepository_Tests : AuditLoggingTestBase + where TStartupModule : IAbpModule + { + protected IAuditLogRepository AuditLogRepository { get; } + protected IGuidGenerator GuidGenerator { get; } + + protected AuditLogRepository_Tests() + { + AuditLogRepository = GetRequiredService(); + GuidGenerator = GetRequiredService(); + } + + [Fact] + public async Task GetListAsync() + { + // Arrange + var userId = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06"); + var userId2 = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06"); + var ipAddress = "153.1.7.61"; + var firstComment = "first Comment"; + + var log1 = new AuditLogInfo + { + UserId = userId, + ImpersonatorUserId = Guid.NewGuid(), + ImpersonatorTenantId = Guid.NewGuid(), + ExecutionTime = DateTime.Today, + ExecutionDuration = 42, + ClientIpAddress = ipAddress, + ClientName = "MyDesktop", + BrowserInfo = "Chrome", + Comments = new List { firstComment, "Second Comment" }, + UserName = "Douglas", + EntityChanges = { + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity", + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + }, + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity", + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + } + + } + }; + + var log2 = new AuditLogInfo + { + UserId = userId2, + ImpersonatorUserId = Guid.NewGuid(), + ImpersonatorTenantId = Guid.NewGuid(), + ExecutionTime = DateTime.Today, + ExecutionDuration = 42, + ClientIpAddress = ipAddress, + ClientName = "MyDesktop", + BrowserInfo = "Chrome", + Comments = new List { firstComment, "Second Comment" }, + HttpStatusCode = (int?)HttpStatusCode.BadGateway, + EntityChanges = { + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity", + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + } + + } + }; + + AuditLogRepository.Insert(new AuditLog(GuidGenerator, log1)); + AuditLogRepository.Insert(new AuditLog(GuidGenerator, log2)); + + //Assert + var logs = await AuditLogRepository.GetListAsync(); + logs.ShouldNotBeNull(); + logs.ShouldContain(x => x.UserId == userId); + logs.ShouldContain(x => x.UserId == userId2); + } + + [Fact] + public async Task GetCountAsync() + { + // Arrange + var userId = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06"); + var userId2 = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06"); + var ipAddress = "153.1.7.61"; + var firstComment = "first Comment"; + + var log1 = new AuditLogInfo + { + UserId = userId, + ImpersonatorUserId = Guid.NewGuid(), + ImpersonatorTenantId = Guid.NewGuid(), + ExecutionTime = DateTime.Today, + ExecutionDuration = 42, + ClientIpAddress = ipAddress, + ClientName = "MyDesktop", + BrowserInfo = "Chrome", + Comments = new List { firstComment, "Second Comment" }, + UserName = "Douglas", + EntityChanges = { + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity", + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + }, + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity", + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + } + + } + }; + + var log2 = new AuditLogInfo + { + UserId = userId2, + ImpersonatorUserId = Guid.NewGuid(), + ImpersonatorTenantId = Guid.NewGuid(), + ExecutionTime = DateTime.Today, + ExecutionDuration = 42, + ClientIpAddress = ipAddress, + ClientName = "MyDesktop", + BrowserInfo = "Chrome", + Comments = new List { firstComment, "Second Comment" }, + HttpStatusCode = (int?)HttpStatusCode.BadGateway, + EntityChanges = { + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity", + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + } + + } + }; + + AuditLogRepository.Insert(new AuditLog(GuidGenerator, log1)); + AuditLogRepository.Insert(new AuditLog(GuidGenerator, log2)); + + //Assert + var logs = await AuditLogRepository.GetCountAsync(); + logs.ShouldBe(2); + } + } +} diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogging_Repository_Resolve_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogging_Repository_Resolve_Tests.cs new file mode 100644 index 0000000000..340e3c7f3c --- /dev/null +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogging_Repository_Resolve_Tests.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AuditLogging +{ + public class AuditLogging_Repository_Resolve_Tests : AuditLoggingTestBase + where TStartupModule : IAbpModule + { + + } +} From ef6523b770934ca50b6d1b6c21b77215acfb6122 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Fri, 5 Apr 2019 08:16:47 +0300 Subject: [PATCH 058/116] Update tr.json --- .../Localization/Resources/Blogging/Web/tr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json index 7d8386e517..dc5df7f0d0 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json @@ -2,11 +2,11 @@ "culture": "tr", "texts": { "Menu:Blogs": "Bloglar", + "Menu:BlogManagement": "Blog Yönetimi", "Title": "Başlık", "Delete": "Sil", "Reply": "Yanıtla", "ReplyTo": "{0}'a cevap ver", - "Menu:BlogManagement": "Blog Yönetimi", "ContinueReading": "Devamı...", "DaysAgo": "{0} Gün Önce", "YearsAgo": "{0} yıl önce", @@ -32,6 +32,7 @@ "BlogDeletionWarningMessage": "Blog silinecek.", "AreYouSure": "Emin misiniz?", "CommentWithCount": "{0} yorum", + "Comment": "Yorum", "CoverImage": "Kapak resmi", "CreateANewPost": "Yeni Yazı oluştur", "CreateANewBlog": "Yeni Blog Ekle", From 5108b041484b23ef59fb34a1f542e7de197b818e Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Fri, 5 Apr 2019 08:20:49 +0300 Subject: [PATCH 059/116] Update tr.json --- .../Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/tr.json | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/tr.json b/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/tr.json index 75510dc78d..504710eaaf 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/tr.json +++ b/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/tr.json @@ -11,7 +11,6 @@ "ShortName": "Kısa isim", "DocumentStoreType": "Döküman saklama tipi", "Format": "Format", - "Actions": "İşlemler", "ShortNameInfoText": "URL oluştururken kullanılacak.", "DisplayName:Name": "Adı", "DisplayName:ShortName": "Kısa adı", From f195d194c703a79d820cc04534fac0ea9c96ae01 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 5 Apr 2019 10:14:42 +0300 Subject: [PATCH 060/116] Implement ValidationInterceptor.InterceptAsync and use ProceedAsync in AuditingInterceptor --- .../Volo/Abp/Auditing/AuditingInterceptor.cs | 2 +- .../Volo/Abp/Validation/ValidationInterceptor.cs | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs index 0ccfb92ffa..434ddc9d16 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs @@ -49,7 +49,7 @@ namespace Volo.Abp.Auditing { if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction)) { - invocation.Proceed(); + await invocation.ProceedAsync(); return; } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs index 4ef688e92e..86d58e89a3 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Aspects; +using System.Threading.Tasks; +using Volo.Abp.Aspects; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; @@ -26,6 +27,19 @@ namespace Volo.Abp.Validation invocation.Proceed(); } + public override async Task InterceptAsync(IAbpMethodInvocation invocation) + { + if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Validation)) + { + await invocation.ProceedAsync(); + return; + } + + Validate(invocation); + + await invocation.ProceedAsync(); + } + protected virtual void Validate(IAbpMethodInvocation invocation) { _validator.Validate( From 74d2100dadad2624b4e2f891bb28a5d33ad1b61e Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 5 Apr 2019 15:22:00 +0800 Subject: [PATCH 061/116] Add blog unit tests. --- modules/blogging/Volo.Blogging.sln | 15 +++- .../Blogging/Tagging/MongoTagRepository.cs | 1 + .../Comments/CommentRepository_Tests.cs | 12 +++ .../Blogging/Posts/PostRepository_Tests.cs | 12 +++ .../Blogging/Tagging/TagRepository_Tests.cs | 12 +++ .../Volo.Blogging.MongoDB.Tests.csproj | 19 +++++ .../Blogging/Blogs/BlogRepository_Tests.cs | 12 +++ .../Comments/CommentRepository_Tests.cs | 12 +++ .../MongoDB/BloggingMongoDBTestModule.cs | 34 +++++++++ .../Blogging/Posts/PostRepository_Tests.cs | 12 +++ .../Blogging/Tagging/TagRepository_Tests.cs | 12 +++ .../Volo/Blogging/BloggingTestData.cs | 2 + .../Volo/Blogging/BloggingTestDataBuilder.cs | 7 +- .../Comments/CommentRepository_Tests.cs | 54 ++++++++++++++ .../Blogging/Posts/PostRepository_Tests.cs | 41 +++++++++++ .../Blogging/Tagging/TagRepository_Tests.cs | 73 +++++++++++++++++++ .../Users/BlogUserRepository_Tests.cs | 13 ++++ 17 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj create mode 100644 modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs create mode 100644 modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Comments/CommentRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Posts/PostRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Tagging/TagRepository_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Users/BlogUserRepository_Tests.cs diff --git a/modules/blogging/Volo.Blogging.sln b/modules/blogging/Volo.Blogging.sln index 05982d4524..fc1319161c 100644 --- a/modules/blogging/Volo.Blogging.sln +++ b/modules/blogging/Volo.Blogging.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2026 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28729.10 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Blogging.Domain.Shared", "src\Volo.Blogging.Domain.Shared\Volo.Blogging.Domain.Shared.csproj", "{F1D954AD-001A-4533-A8CC-94DDCF70B552}" EndProject @@ -35,9 +35,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Blogging.EntityFramewo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Blogging.Application.Tests", "test\Volo.Blogging.Application.Tests\Volo.Blogging.Application.Tests.csproj", "{C949B953-80B3-4B36-B535-1AD74A34FEAC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Blogging.MongoDB", "src\Volo.Blogging.MongoDB\Volo.Blogging.MongoDB.csproj", "{98C2D36A-F874-405D-8565-9CE59438E879}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Blogging.MongoDB", "src\Volo.Blogging.MongoDB\Volo.Blogging.MongoDB.csproj", "{98C2D36A-F874-405D-8565-9CE59438E879}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.BloggingTestApp.MongoDb", "app\Volo.BloggingTestApp.MongoDb\Volo.BloggingTestApp.MongoDb.csproj", "{4C818374-2DE8-422F-8585-975E8366DB26}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BloggingTestApp.MongoDb", "app\Volo.BloggingTestApp.MongoDb\Volo.BloggingTestApp.MongoDb.csproj", "{4C818374-2DE8-422F-8585-975E8366DB26}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Blogging.MongoDB.Tests", "test\Volo.Blogging.MongoDB.Tests\Volo.Blogging.MongoDB.Tests.csproj", "{0A29F64C-11F1-40B6-8E6D-91E86823775E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -105,6 +107,10 @@ Global {4C818374-2DE8-422F-8585-975E8366DB26}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C818374-2DE8-422F-8585-975E8366DB26}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C818374-2DE8-422F-8585-975E8366DB26}.Release|Any CPU.Build.0 = Release|Any CPU + {0A29F64C-11F1-40B6-8E6D-91E86823775E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A29F64C-11F1-40B6-8E6D-91E86823775E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A29F64C-11F1-40B6-8E6D-91E86823775E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A29F64C-11F1-40B6-8E6D-91E86823775E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -125,6 +131,7 @@ Global {C949B953-80B3-4B36-B535-1AD74A34FEAC} = {25B3A516-5C0D-42E3-9294-E8A9346CEE4B} {98C2D36A-F874-405D-8565-9CE59438E879} = {42BF26EF-B8C7-42DC-9FFB-3653109B7776} {4C818374-2DE8-422F-8585-975E8366DB26} = {EB4FB44A-FE39-4245-9DAD-D6437BCE3870} + {0A29F64C-11F1-40B6-8E6D-91E86823775E} = {25B3A516-5C0D-42E3-9294-E8A9346CEE4B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F2BAE819-78D4-407A-9201-22473B2850B0} diff --git a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Tagging/MongoTagRepository.cs b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Tagging/MongoTagRepository.cs index 1dedad184b..e2fcdca96c 100644 --- a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Tagging/MongoTagRepository.cs +++ b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Tagging/MongoTagRepository.cs @@ -44,6 +44,7 @@ namespace Volo.Blogging.Tagging foreach (var tag in tags) { tag.DecreaseUsageCount(); + Update(tag); } } } diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs new file mode 100644 index 0000000000..2765479e2f --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Blogging.EntityFrameworkCore; + +namespace Volo.Blogging.Comments +{ + public class CommentRepository_Tests : CommentRepository_Tests + { + + } +} diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs new file mode 100644 index 0000000000..22dd376aaf --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Blogging.EntityFrameworkCore; + +namespace Volo.Blogging.Posts +{ + public class PostRepository_Tests : PostRepository_Tests + { + + } +} diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs new file mode 100644 index 0000000000..65ebebd4ff --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Blogging.EntityFrameworkCore; + +namespace Volo.Blogging.Tagging +{ + public class TagRepository_Tests : TagRepository_Tests + { + + } +} diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj new file mode 100644 index 0000000000..c3c24c1e05 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs new file mode 100644 index 0000000000..3cf6c37080 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Blogging.Blogs; + +namespace Volo.Blogging.MongoDB +{ + public class BlogRepository_Tests : BlogRepository_Tests + { + + } +} diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs new file mode 100644 index 0000000000..8bf936d9df --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Blogging.Comments; + +namespace Volo.Blogging.MongoDB +{ + public class CommentRepository_Tests : CommentRepository_Tests + { + + } +} diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs new file mode 100644 index 0000000000..7ac35300ab --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Mongo2Go; +using Volo.Abp; +using Volo.Abp.Data; +using Volo.Abp.Modularity; + +namespace Volo.Blogging.MongoDB +{ + [DependsOn( + typeof(BloggingTestBaseModule), + typeof(BloggingMongoDbModule) + )] + public class BloggingMongoDBTestModule : AbpModule + { + private MongoDbRunner _mongoDbRunner; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + _mongoDbRunner = MongoDbRunner.Start(); + + Configure(options => + { + options.ConnectionStrings.Default = _mongoDbRunner.ConnectionString; + }); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + _mongoDbRunner.Dispose(); + } + } +} diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs new file mode 100644 index 0000000000..b392d99a05 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Blogging.Posts; + +namespace Volo.Blogging.MongoDB.Tests.Volo.Blogging.Posts +{ + public class PostRepository_Tests : PostRepository_Tests + { + + } +} diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs new file mode 100644 index 0000000000..0fbc8b9d10 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Blogging.Tagging; + +namespace Volo.Blogging.MongoDB +{ + public class TagRepository_Tests : TagRepository_Tests + { + + } +} diff --git a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/BloggingTestData.cs b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/BloggingTestData.cs index 72910fb6ca..9666201719 100644 --- a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/BloggingTestData.cs +++ b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/BloggingTestData.cs @@ -9,6 +9,8 @@ namespace Volo.Blogging public Guid Blog1Post1Id { get; } = Guid.NewGuid(); public Guid Blog1Post2Id { get; } = Guid.NewGuid(); public Guid Blog1Post1Comment1Id { get; } = Guid.NewGuid(); + public Guid Blog1Post1Comment2Id { get; } = Guid.NewGuid(); public string Tag1Name { get; } = "Tag1Name"; + public string Tag2Name { get; } = "Tag2Name"; } } \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/BloggingTestDataBuilder.cs b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/BloggingTestDataBuilder.cs index b830887fdd..9747478aa6 100644 --- a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/BloggingTestDataBuilder.cs +++ b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/BloggingTestDataBuilder.cs @@ -40,9 +40,12 @@ namespace Volo.Blogging { await _blogRepository.InsertAsync(new Blog(_testData.Blog1Id, "The First Blog", "blog-1")); await _postRepository.InsertAsync(new Post(_testData.Blog1Post1Id, _testData.Blog1Id, Guid.Empty, "title", "coverImage", "url")); - await _postRepository.InsertAsync(new Post(_testData.Blog1Post2Id, _testData.Blog1Id, Guid.Empty, "title", "coverImage", "url")); + await _postRepository.InsertAsync(new Post(_testData.Blog1Post2Id, _testData.Blog1Id, Guid.Empty, "title2", "coverImage2", "url2")); await _commentRepository.InsertAsync(new Comment(_testData.Blog1Post1Comment1Id,_testData.Blog1Post1Id,null,"text")); - await _tagRepository.InsertAsync(new Tag(_testData.Blog1Id, _testData.Tag1Name)); + await _commentRepository.InsertAsync(new Comment(_testData.Blog1Post1Comment2Id, _testData.Blog1Post1Id, _testData.Blog1Post1Comment1Id, "text")); + await _tagRepository.InsertAsync(new Tag(_testData.Blog1Id, _testData.Tag1Name, 10)); + await _tagRepository.InsertAsync(new Tag(_testData.Blog1Id, _testData.Tag2Name)); + } } } diff --git a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Comments/CommentRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Comments/CommentRepository_Tests.cs new file mode 100644 index 0000000000..04db9d9f0b --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Comments/CommentRepository_Tests.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Blogging.Comments +{ + public abstract class CommentRepository_Tests : BloggingTestBase + where TStartupModule : IAbpModule + { + protected ICommentRepository CommentRepository { get; } + protected BloggingTestData BloggingTestData { get; } + + protected CommentRepository_Tests() + { + CommentRepository = GetRequiredService(); + BloggingTestData = GetRequiredService(); + } + + [Fact] + public async Task GetListOfPostAsync() + { + var comments = await CommentRepository.GetListOfPostAsync(BloggingTestData.Blog1Post1Id); + comments.ShouldNotBeNull(); + comments.Count.ShouldBe(2); + comments.ShouldAllBe(x => x.PostId == BloggingTestData.Blog1Post1Id); + } + + [Fact] + public async Task GetCommentCountOfPostAsync() + { + var count = await CommentRepository.GetCommentCountOfPostAsync(BloggingTestData.Blog1Post1Id); + count.ShouldBe(2); + } + + [Fact] + public async Task GetRepliesOfComment() + { + var comment = await CommentRepository.GetRepliesOfComment(BloggingTestData.Blog1Post1Comment1Id); + comment.ShouldNotBeNull(); + comment.ShouldContain(x => x.Id == BloggingTestData.Blog1Post1Comment2Id); + } + + [Fact] + public async Task DeleteOfPost() + { + await CommentRepository.DeleteOfPost(BloggingTestData.Blog1Post1Id); + (await CommentRepository.GetListAsync()).ShouldBeEmpty(); + } + } +} diff --git a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Posts/PostRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Posts/PostRepository_Tests.cs new file mode 100644 index 0000000000..ee21e200d9 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Posts/PostRepository_Tests.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Blogging.Posts +{ + public abstract class PostRepository_Tests : BloggingTestBase + where TStartupModule : IAbpModule + { + protected IPostRepository PostRepository { get; } + protected BloggingTestData BloggingTestData { get; } + + protected PostRepository_Tests() + { + PostRepository = GetRequiredService(); + BloggingTestData = GetRequiredService(); + } + + [Fact] + public async Task GetListOfPostAsync() + { + var posts = await PostRepository.GetPostsByBlogId(BloggingTestData.Blog1Id); + posts.ShouldNotBeNull(); + posts.Count.ShouldBe(2); + posts.ShouldContain(x => x.Id == BloggingTestData.Blog1Post1Id); + posts.ShouldContain(x => x.Id == BloggingTestData.Blog1Post2Id); + } + + [Fact] + public async Task GetPostByUrl() + { + var post = await PostRepository.GetPostByUrl(BloggingTestData.Blog1Id, "url"); + post.ShouldNotBeNull(); + post.Url.ShouldBe("url"); + } + } +} diff --git a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Tagging/TagRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Tagging/TagRepository_Tests.cs new file mode 100644 index 0000000000..e1752ec200 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Tagging/TagRepository_Tests.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Blogging.Tagging +{ + public abstract class TagRepository_Tests : BloggingTestBase + where TStartupModule : IAbpModule + { + protected ITagRepository TagRepository { get; } + protected BloggingTestData BloggingTestData { get; } + + protected TagRepository_Tests() + { + TagRepository = GetRequiredService(); + BloggingTestData = GetRequiredService(); + } + + [Fact] + public async Task GetListAsync() + { + var tags = await TagRepository.GetListAsync(BloggingTestData.Blog1Id); + tags.ShouldNotBeNull(); + tags.Count.ShouldBe(2); + } + + [Fact] + public async Task GetByNameAsync() + { + var tag = await TagRepository.GetByNameAsync(BloggingTestData.Blog1Id, BloggingTestData.Tag1Name); + tag.ShouldNotBeNull(); + tag.Name.ShouldBe(BloggingTestData.Tag1Name); + } + + [Fact] + public async Task FindByNameAsync() + { + var tag = await TagRepository.FindByNameAsync(BloggingTestData.Blog1Id, BloggingTestData.Tag1Name); + tag.ShouldNotBeNull(); + tag.Name.ShouldBe(BloggingTestData.Tag1Name); + } + + [Fact] + public async Task GetListAsync2() + { + var tagIds = (await TagRepository.GetListAsync()).Select(x => x.Id).ToList(); + var tags = await TagRepository.GetListAsync(tagIds); + tags.ShouldNotBeNull(); + tags.Count.ShouldBe(tagIds.Count); + } + + [Fact] + public async Task DecreaseUsageCountOfTags() + { + var tag = await TagRepository.FindByNameAsync(BloggingTestData.Blog1Id, BloggingTestData.Tag1Name); + var usageCount = tag.UsageCount; + + TagRepository.DecreaseUsageCountOfTags(new List() + { + tag.Id + }); + + var qq = await TagRepository.FindByNameAsync(BloggingTestData.Blog1Id, BloggingTestData.Tag1Name); + (await TagRepository.FindByNameAsync(BloggingTestData.Blog1Id, BloggingTestData.Tag1Name)).UsageCount + .ShouldBe(usageCount - 1); + } + } +} diff --git a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Users/BlogUserRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Users/BlogUserRepository_Tests.cs new file mode 100644 index 0000000000..3f0e3b7bf7 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Users/BlogUserRepository_Tests.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Abp.Modularity; + +namespace Volo.Blogging.Users +{ + public abstract class BlogUserRepository_Tests : BloggingTestBase + where TStartupModule : IAbpModule + { + + } +} From 235ec8268de74eb48aeec93803d76ffc986ad549 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 5 Apr 2019 17:08:31 +0800 Subject: [PATCH 062/116] Add test layer. --- modules/docs/Volo.Docs.sln | 41 +++++++++++++++++- .../Volo.Docs.Admin.Application.Tests.csproj | 17 ++++++++ .../Docs/DocsAdminApplicationTestModule.cs | 14 +++++++ .../Volo.Docs.Application.Tests.csproj | 17 ++++++++ .../Volo/Docs/DocsApplicationTestModule.cs | 13 ++++++ .../Volo.Docs.Domain.Tests.csproj | 17 ++++++++ .../Volo/Docs/DocsDomainTestBase.cs | 7 ++++ .../Volo/Docs/DocsDomainTestModule.cs | 13 ++++++ ...Volo.Docs.EntityFrameworkCore.Tests.csproj | 20 +++++++++ .../DocsEntityFrameworkCoreTestModule.cs | 42 +++++++++++++++++++ .../Volo.Docs.TestBase.csproj | 26 ++++++++++++ .../Volo/Docs/DocsTestBase.cs | 14 +++++++ .../Volo/Docs/DocsTestBaseModule.cs | 37 ++++++++++++++++ .../Volo/Docs/DocsTestData.cs | 8 ++++ .../Volo/Docs/DocsTestDataBuilder.cs | 24 +++++++++++ 15 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj create mode 100644 modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsAdminApplicationTestModule.cs create mode 100644 modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj create mode 100644 modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs create mode 100644 modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj create mode 100644 modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs create mode 100644 modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestModule.cs create mode 100644 modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj create mode 100644 modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs create mode 100644 modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj create mode 100644 modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBase.cs create mode 100644 modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBaseModule.cs create mode 100644 modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestData.cs create mode 100644 modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestDataBuilder.cs diff --git a/modules/docs/Volo.Docs.sln b/modules/docs/Volo.Docs.sln index 7e78d7432f..0c3b43a821 100644 --- a/modules/docs/Volo.Docs.sln +++ b/modules/docs/Volo.Docs.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28729.10 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{42416152-5BAB-4706-93A6-57A19E71FE14}" EndProject @@ -43,6 +43,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Admin.HttpApi.Cli EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Admin.Web", "src\Volo.Docs.Admin.Web\Volo.Docs.Admin.Web.csproj", "{116A6145-9D66-4867-B3EF-A464FAC47946}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{59D430A9-AC61-4457-8338-5DA0705ABB5D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Application.Tests", "test\Volo.Docs.Application.Tests\Volo.Docs.Application.Tests.csproj", "{1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Domain.Tests", "test\Volo.Docs.Domain.Tests\Volo.Docs.Domain.Tests.csproj", "{213F44A8-F9C1-4F04-9159-37E232FF18F2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.EntityFrameworkCore.Tests", "test\Volo.Docs.EntityFrameworkCore.Tests\Volo.Docs.EntityFrameworkCore.Tests.csproj", "{89F895EA-C4A0-4D91-9181-016F78459776}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.TestBase", "test\Volo.Docs.TestBase\Volo.Docs.TestBase.csproj", "{C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Admin.Application.Tests", "test\Volo.Docs.Admin.Application.Tests\Volo.Docs.Admin.Application.Tests.csproj", "{E9CF69BC-EEA6-4621-BE0E-64EE37C89807}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -109,6 +121,26 @@ Global {116A6145-9D66-4867-B3EF-A464FAC47946}.Debug|Any CPU.Build.0 = Debug|Any CPU {116A6145-9D66-4867-B3EF-A464FAC47946}.Release|Any CPU.ActiveCfg = Release|Any CPU {116A6145-9D66-4867-B3EF-A464FAC47946}.Release|Any CPU.Build.0 = Release|Any CPU + {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}.Release|Any CPU.Build.0 = Release|Any CPU + {213F44A8-F9C1-4F04-9159-37E232FF18F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {213F44A8-F9C1-4F04-9159-37E232FF18F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {213F44A8-F9C1-4F04-9159-37E232FF18F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {213F44A8-F9C1-4F04-9159-37E232FF18F2}.Release|Any CPU.Build.0 = Release|Any CPU + {89F895EA-C4A0-4D91-9181-016F78459776}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89F895EA-C4A0-4D91-9181-016F78459776}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89F895EA-C4A0-4D91-9181-016F78459776}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89F895EA-C4A0-4D91-9181-016F78459776}.Release|Any CPU.Build.0 = Release|Any CPU + {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}.Release|Any CPU.Build.0 = Release|Any CPU + {E9CF69BC-EEA6-4621-BE0E-64EE37C89807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9CF69BC-EEA6-4621-BE0E-64EE37C89807}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9CF69BC-EEA6-4621-BE0E-64EE37C89807}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9CF69BC-EEA6-4621-BE0E-64EE37C89807}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -132,6 +164,11 @@ Global {262F38DB-62AF-427F-96E2-C6385C5AB695} = {BCA19441-17E9-43E6-AED1-15344D18F967} {81EE378A-0DE1-47BA-86D9-08EF6317BB95} = {BCA19441-17E9-43E6-AED1-15344D18F967} {116A6145-9D66-4867-B3EF-A464FAC47946} = {BCA19441-17E9-43E6-AED1-15344D18F967} + {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3} = {59D430A9-AC61-4457-8338-5DA0705ABB5D} + {213F44A8-F9C1-4F04-9159-37E232FF18F2} = {59D430A9-AC61-4457-8338-5DA0705ABB5D} + {89F895EA-C4A0-4D91-9181-016F78459776} = {59D430A9-AC61-4457-8338-5DA0705ABB5D} + {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2} = {59D430A9-AC61-4457-8338-5DA0705ABB5D} + {E9CF69BC-EEA6-4621-BE0E-64EE37C89807} = {59D430A9-AC61-4457-8338-5DA0705ABB5D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {13691265-2547-4FFF-B757-E8FACB05679D} diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj new file mode 100644 index 0000000000..390476a38f --- /dev/null +++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsAdminApplicationTestModule.cs b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsAdminApplicationTestModule.cs new file mode 100644 index 0000000000..b3198c61ff --- /dev/null +++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsAdminApplicationTestModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Modularity; +using Volo.Docs.Admin; + +namespace Volo.Docs +{ + [DependsOn( + typeof(DocsAdminApplicationModule), + typeof(DocsDomainTestModule) + )] + public class DocsApplicationTestModule : AbpModule + { + + } +} diff --git a/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj b/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj new file mode 100644 index 0000000000..87144ccfe9 --- /dev/null +++ b/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + diff --git a/modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs b/modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs new file mode 100644 index 0000000000..62d027c220 --- /dev/null +++ b/modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Modularity; + +namespace Volo.Docs +{ + [DependsOn( + typeof(DocsApplicationModule), + typeof(DocsDomainTestModule) + )] + public class DocsApplicationTestModule : AbpModule + { + + } +} diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj b/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj new file mode 100644 index 0000000000..ba1185b397 --- /dev/null +++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs new file mode 100644 index 0000000000..1a34d74ddf --- /dev/null +++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs @@ -0,0 +1,7 @@ +namespace Volo.Docs +{ + public abstract class DocsDomainTestBase : DocsTestBase + { + + } +} \ No newline at end of file diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestModule.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestModule.cs new file mode 100644 index 0000000000..df4d32e52b --- /dev/null +++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestModule.cs @@ -0,0 +1,13 @@ +using Volo.Docs.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Docs +{ + [DependsOn( + typeof(DocsEntityFrameworkCoreTestModule) + )] + public class DocsDomainTestModule : AbpModule + { + + } +} diff --git a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj new file mode 100644 index 0000000000..666c1e813f --- /dev/null +++ b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + + + + diff --git a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs new file mode 100644 index 0000000000..49448f0fdd --- /dev/null +++ b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs @@ -0,0 +1,42 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Docs.EntityFrameworkCore +{ + [DependsOn( + typeof(DocsTestBaseModule), + typeof(DocsEntityFrameworkCoreModule) + )] + public class DocsEntityFrameworkCoreTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var sqliteConnection = CreateDatabaseAndGetConnection(); + + Configure(options => + { + options.Configure(abpDbContextConfigurationContext => + { + abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection); + }); + }); + } + + private static SqliteConnection CreateDatabaseAndGetConnection() + { + var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + new DocsDbContext( + new DbContextOptionsBuilder().UseSqlite(connection).Options + ).GetService().CreateTables(); + + return connection; + } + } +} diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj b/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj new file mode 100644 index 0000000000..a92946140a --- /dev/null +++ b/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj @@ -0,0 +1,26 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBase.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBase.cs new file mode 100644 index 0000000000..2ceb4b8f95 --- /dev/null +++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBase.cs @@ -0,0 +1,14 @@ +using Volo.Abp; +using Volo.Abp.Modularity; + +namespace Volo.Docs +{ + public abstract class DocsTestBase : AbpIntegratedTest + where TStartupModule : IAbpModule + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBaseModule.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBaseModule.cs new file mode 100644 index 0000000000..a4661075d0 --- /dev/null +++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBaseModule.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Authorization; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Docs +{ + [DependsOn( + typeof(AbpAutofacModule), + typeof(AbpTestBaseModule), + typeof(AbpAuthorizationModule), + typeof(DocsDomainModule) + )] + public class DocsTestBaseModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAlwaysAllowAuthorization(); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + SeedTestData(context); + } + + private static void SeedTestData(ApplicationInitializationContext context) + { + using (var scope = context.ServiceProvider.CreateScope()) + { + scope.ServiceProvider + .GetRequiredService() + .Build(); + } + } + } +} diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestData.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestData.cs new file mode 100644 index 0000000000..c6947a74f2 --- /dev/null +++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestData.cs @@ -0,0 +1,8 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Docs +{ + public class DocsTestData : ISingletonDependency + { + } +} diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestDataBuilder.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestDataBuilder.cs new file mode 100644 index 0000000000..6c8e769103 --- /dev/null +++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestDataBuilder.cs @@ -0,0 +1,24 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; + +namespace Volo.Docs +{ + public class DocsTestDataBuilder : ITransientDependency + { + private readonly IGuidGenerator _guidGenerator; + private DocsTestData _testData; + + public DocsTestDataBuilder( + IGuidGenerator guidGenerator, + DocsTestData testData) + { + _guidGenerator = guidGenerator; + _testData = testData; + } + + public void Build() + { + + } + } +} \ No newline at end of file From d3a5224240b5f2fe57c908357a67499835e3edb7 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 5 Apr 2019 13:53:10 +0300 Subject: [PATCH 063/116] Upgrade to Castle.Core 4.4.0. --- framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj b/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj index f895812fab..adcf4f8c2f 100644 --- a/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj +++ b/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj @@ -14,7 +14,7 @@ - + From 1da13a0a68f5d93477b5ce2b382f78923dedb2b0 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 5 Apr 2019 14:55:54 +0300 Subject: [PATCH 064/116] Use new CaptureProceedInfo feature of Castle.Core --- .../Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs b/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs index 567fc640f8..5fd4270854 100644 --- a/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs +++ b/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs @@ -30,17 +30,19 @@ namespace Volo.Abp.Castle.DynamicProxy private object _actualReturnValue; protected IInvocation Invocation { get; } + protected IInvocationProceedInfo ProceedInfo { get; } public CastleAbpMethodInvocationAdapter(IInvocation invocation) { Invocation = invocation; + ProceedInfo = invocation.CaptureProceedInfo(); _lazyArgumentsDictionary = new Lazy>(GetArgumentsDictionary); } public void Proceed() { - Invocation.Proceed(); + ProceedInfo.Invoke(); if (Invocation.Method.IsAsync()) { @@ -50,7 +52,7 @@ namespace Volo.Abp.Castle.DynamicProxy public Task ProceedAsync() { - Invocation.Proceed(); + ProceedInfo.Invoke(); _actualReturnValue = Invocation.ReturnValue; return Invocation.Method.IsAsync() ? (Task)_actualReturnValue From a6c744cd67f4b4bc2e07a62551879fc43f176258 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 5 Apr 2019 21:28:51 +0800 Subject: [PATCH 065/116] Add blog entity unit tests. --- modules/blogging/Volo.Blogging.sln | 9 ++- .../Volo/Blogging/Tagging/Tag.cs | 7 ++ .../Volo.Blogging.Application.Tests.csproj | 2 +- .../Volo.Blogging.Domain.Tests.csproj | 18 +++++ .../Abp/Blogging/BloggingDomainTestBase.cs | 11 +++ .../Abp/Blogging/BloggingDomainTestModule.cs | 12 ++++ .../Volo/Abp/Blogging/Blogs/Blog_Tests.cs | 32 +++++++++ .../Abp/Blogging/Comments/Comment_Tests.cs | 22 ++++++ .../Volo/Abp/Blogging/Posts/Post_Tests.cs | 65 +++++++++++++++++ .../Volo/Abp/Blogging/Tagging/Tag_Tests.cs | 64 +++++++++++++++++ .../Volo/Abp/Blogging/Users/BlogUser_Test.cs | 71 +++++++++++++++++++ .../Blogging/Blogs/BlogRepository_Tests.cs | 7 +- .../Comments/CommentRepository_Tests.cs | 10 +-- .../BloggingEntityFrameworkCoreTestModule.cs | 6 +- .../Blogging/Posts/PostRepository_Tests.cs | 10 +-- .../Blogging/Tagging/TagRepository_Tests.cs | 10 +-- .../Volo.Blogging.MongoDB.Tests.csproj | 2 +- .../Blogging/Blogs/BlogRepository_Tests.cs | 10 +-- .../Comments/CommentRepository_Tests.cs | 10 +-- .../MongoDB/BloggingMongoDBTestModule.cs | 10 ++- .../Blogging/Posts/PostRepository_Tests.cs | 10 +-- .../Blogging/Tagging/TagRepository_Tests.cs | 10 +-- .../Blogging/Tagging/TagRepository_Tests.cs | 2 +- 23 files changed, 344 insertions(+), 66 deletions(-) create mode 100644 modules/blogging/test/Volo.Blogging.Domain.Tests/Volo.Blogging.Domain.Tests.csproj create mode 100644 modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestBase.cs create mode 100644 modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestModule.cs create mode 100644 modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Blogs/Blog_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Comments/Comment_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Posts/Post_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Tagging/Tag_Tests.cs create mode 100644 modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Users/BlogUser_Test.cs diff --git a/modules/blogging/Volo.Blogging.sln b/modules/blogging/Volo.Blogging.sln index fc1319161c..1c4ac401bb 100644 --- a/modules/blogging/Volo.Blogging.sln +++ b/modules/blogging/Volo.Blogging.sln @@ -39,7 +39,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Blogging.MongoDB", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BloggingTestApp.MongoDb", "app\Volo.BloggingTestApp.MongoDb\Volo.BloggingTestApp.MongoDb.csproj", "{4C818374-2DE8-422F-8585-975E8366DB26}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Blogging.MongoDB.Tests", "test\Volo.Blogging.MongoDB.Tests\Volo.Blogging.MongoDB.Tests.csproj", "{0A29F64C-11F1-40B6-8E6D-91E86823775E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Blogging.MongoDB.Tests", "test\Volo.Blogging.MongoDB.Tests\Volo.Blogging.MongoDB.Tests.csproj", "{0A29F64C-11F1-40B6-8E6D-91E86823775E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Blogging.Domain.Tests", "test\Volo.Blogging.Domain.Tests\Volo.Blogging.Domain.Tests.csproj", "{B6D7EF20-9921-490A-AA95-47E3E174DC9B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -111,6 +113,10 @@ Global {0A29F64C-11F1-40B6-8E6D-91E86823775E}.Debug|Any CPU.Build.0 = Debug|Any CPU {0A29F64C-11F1-40B6-8E6D-91E86823775E}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A29F64C-11F1-40B6-8E6D-91E86823775E}.Release|Any CPU.Build.0 = Release|Any CPU + {B6D7EF20-9921-490A-AA95-47E3E174DC9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6D7EF20-9921-490A-AA95-47E3E174DC9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6D7EF20-9921-490A-AA95-47E3E174DC9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6D7EF20-9921-490A-AA95-47E3E174DC9B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -132,6 +138,7 @@ Global {98C2D36A-F874-405D-8565-9CE59438E879} = {42BF26EF-B8C7-42DC-9FFB-3653109B7776} {4C818374-2DE8-422F-8585-975E8366DB26} = {EB4FB44A-FE39-4245-9DAD-D6437BCE3870} {0A29F64C-11F1-40B6-8E6D-91E86823775E} = {25B3A516-5C0D-42E3-9294-E8A9346CEE4B} + {B6D7EF20-9921-490A-AA95-47E3E174DC9B} = {25B3A516-5C0D-42E3-9294-E8A9346CEE4B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F2BAE819-78D4-407A-9201-22473B2850B0} diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/Tag.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/Tag.cs index f89d5f981f..28d14d885d 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/Tag.cs +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/Tag.cs @@ -44,6 +44,13 @@ namespace Volo.Blogging.Tagging { return; } + + if (UsageCount - number <= 0) + { + UsageCount = 0; + return; + } + UsageCount -= number; } diff --git a/modules/blogging/test/Volo.Blogging.Application.Tests/Volo.Blogging.Application.Tests.csproj b/modules/blogging/test/Volo.Blogging.Application.Tests/Volo.Blogging.Application.Tests.csproj index aa2837f443..a36f95ac50 100644 --- a/modules/blogging/test/Volo.Blogging.Application.Tests/Volo.Blogging.Application.Tests.csproj +++ b/modules/blogging/test/Volo.Blogging.Application.Tests/Volo.Blogging.Application.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo.Blogging.Domain.Tests.csproj b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo.Blogging.Domain.Tests.csproj new file mode 100644 index 0000000000..b2911bddee --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo.Blogging.Domain.Tests.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestBase.cs b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestBase.cs new file mode 100644 index 0000000000..7d758ee699 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestBase.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Blogging +{ + public abstract class BloggingDomainTestBase : BloggingTestBase + { + + } +} diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestModule.cs b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestModule.cs new file mode 100644 index 0000000000..01ab4b07c7 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestModule.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Modularity; + +namespace Volo.Blogging +{ + [DependsOn( + typeof(BloggingEntityFrameworkCoreTestModule), + typeof(BloggingTestBaseModule) + )] + public class BloggingDomainTestModule : AbpModule + { + } +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Blogs/Blog_Tests.cs b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Blogs/Blog_Tests.cs new file mode 100644 index 0000000000..6c44885dde --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Blogs/Blog_Tests.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Shouldly; +using Volo.Blogging.Blogs; +using Xunit; + +namespace Volo.Blogging +{ + public class Blog_Tests + { + [Theory] + [InlineData("aaa")] + [InlineData("bbb")] + public void SetName(string name) + { + var blog = new Blog(Guid.NewGuid(), "test blog", "test"); + blog.SetName(name); + blog.Name.ShouldBe(name); + } + + [Theory] + [InlineData("aaa")] + [InlineData("bbb")] + public void SetShortName(string name) + { + var blog = new Blog(Guid.NewGuid(), "test blog", "test"); + blog.SetShortName(name); + blog.ShortName.ShouldBe(name); + } + } +} diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Comments/Comment_Tests.cs b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Comments/Comment_Tests.cs new file mode 100644 index 0000000000..34195d5f81 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Comments/Comment_Tests.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Shouldly; +using Volo.Blogging.Comments; +using Xunit; + +namespace Volo.Blogging +{ + public class Comment_Tests + { + [Theory] + [InlineData("aaa")] + [InlineData("bbb")] + public void SetText(string text) + { + var comment = new Comment(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "good"); + comment.SetText(text); + comment.Text.ShouldBe(text); + } + } +} diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Posts/Post_Tests.cs b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Posts/Post_Tests.cs new file mode 100644 index 0000000000..91aede46a7 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Posts/Post_Tests.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Shouldly; +using Volo.Blogging.Comments; +using Volo.Blogging.Posts; +using Xunit; + +namespace Volo.Blogging +{ + public class Post_Tests + { + [Fact] + public void IncreaseReadCount() + { + var post = new Post(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "abp", "⊙o⊙", "abp.io"); + post.IncreaseReadCount(); + post.ReadCount.ShouldBe(1); + } + + [Theory] + [InlineData("aaa")] + [InlineData("bbb")] + public void SetTitle(string title) + { + var post = new Post(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "abp", "⊙o⊙", "abp.io"); + post.SetTitle(title); + post.Title.ShouldBe(title); + } + + [Theory] + [InlineData("aaa")] + [InlineData("bbb")] + public void SetUrl(string url) + { + var post = new Post(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "abp", "⊙o⊙", "abp.io"); + post.SetUrl(url); + post.Url.ShouldBe(url); + } + + [Fact] + public void AddTag() + { + var post = new Post(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "abp", "⊙o⊙", "abp.io"); + var tagId = Guid.NewGuid(); + post.AddTag(tagId); + post.Tags.ShouldContain(x => x.TagId == tagId); + } + + + [Fact] + public void RemoveTag() + { + var post = new Post(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "abp", "⊙o⊙", "abp.io"); + var tagId = Guid.NewGuid(); + post.AddTag(tagId); + + post.Tags.ShouldContain(x => x.TagId == tagId); + + post.RemoveTag(tagId); + post.Tags.ShouldNotContain(x => x.TagId == tagId); + } + + } +} diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Tagging/Tag_Tests.cs b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Tagging/Tag_Tests.cs new file mode 100644 index 0000000000..a4368a6341 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Tagging/Tag_Tests.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Shouldly; +using Volo.Blogging.Tagging; +using Xunit; + +namespace Volo.Blogging +{ + public class Tag_Tests + { + [Theory] + [InlineData("aaa")] + [InlineData("bbb")] + public void SetName(string name) + { + var tag = new Tag(Guid.NewGuid(), "abp", 0, "abp tag"); + tag.SetName(name); + tag.Name.ShouldBe(name); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void IncreaseUsageCount(int number) + { + var tag = new Tag(Guid.NewGuid(), "abp", 0, "abp tag"); + tag.IncreaseUsageCount(number); + tag.UsageCount.ShouldBe(number); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void DecreaseUsageCount(int number) + { + var tag = new Tag(Guid.NewGuid(), "abp", 10, "abp tag"); + tag.DecreaseUsageCount(number); + tag.UsageCount.ShouldBe(10 - number); + } + + [Fact] + public void DecreaseUsageCount_Should_Greater_ThanOrEqual_Zero() + { + var tag = new Tag(Guid.NewGuid(), "abp", 10, "abp tag"); + tag.DecreaseUsageCount(100); + tag.UsageCount.ShouldBe(0); + } + + [Theory] + [InlineData("aaa")] + [InlineData("bbb")] + public void SetDescription(string description) + { + var tag = new Tag(Guid.NewGuid(), "abp", 0, "abp tag"); + tag.SetDescription(description); + tag.Description.ShouldBe(description); + } + + + } +} diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Users/BlogUser_Test.cs b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Users/BlogUser_Test.cs new file mode 100644 index 0000000000..e407d6f3f0 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/Users/BlogUser_Test.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; +using Shouldly; +using Volo.Abp.Users; +using Volo.Blogging.Users; +using Xunit; + +namespace Volo.Blogging +{ + public class BlogUser_Test + { + [Fact] + public void Update() + { + var userId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + var blogUser = new BlogUser(new UserData(userId, "bob lee", "boblee@volosoft.com", "lee", "bob", true, + "123456", true, tenantId)); + var userData = new UserData(userId, "lee bob", "leebob@volosoft.com", "bob", "lee", false, + "654321", false, tenantId); + + blogUser.Update(userData); + + blogUser.Equals(new BlogUser(userData)).ShouldBeTrue(); + } + + [Fact] + public void Update_User_Id_Must_Equals() + { + var userId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + var blogUser = new BlogUser(new UserData(userId, "bob lee", "boblee@volosoft.com", "lee", "bob", true, + "123456", true, tenantId)); + + var userData = new UserData(Guid.NewGuid(), "lee bob", "leebob@volosoft.com", "bob", "lee", false, + "654321", false, tenantId); + + Assert.Throws(() => blogUser.Update(userData)); + } + + [Fact] + public void Update_Tenant_Id_Must_Equals() + { + var userId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + var blogUser = new BlogUser(new UserData(userId, "bob lee", "boblee@volosoft.com", "lee", "bob", true, + "123456", true, tenantId)); + + var userData = new UserData(userId, "lee bob", "leebob@volosoft.com", "bob", "lee", false, + "654321", false, Guid.NewGuid()); + + Assert.Throws(() => blogUser.Update(userData)); + } + + [Fact] + public void BlogUserEquals() + { + var userId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + var blogUser = new BlogUser(new UserData(userId, "bob lee", "john@volosoft.com", "lee", "bob", true, + "123456", true, tenantId)); + + var blogUser2= new BlogUser(new UserData(userId, "bob lee", "john@volosoft.com", "lee", "bob", true, + "123456", true, tenantId)); + + blogUser.Equals(blogUser2).ShouldBeTrue(); + } + } +} diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs index b86945418c..4735926cc2 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs @@ -1,9 +1,8 @@ -using Volo.Blogging.EntityFrameworkCore; +using Volo.Blogging.Blogs; -namespace Volo.Blogging.Blogs +namespace Volo.Blogging { public class BlogRepository_Tests : BlogRepository_Tests { - } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs index 2765479e2f..5013e633fb 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Volo.Blogging.EntityFrameworkCore; +using Volo.Blogging.Comments; -namespace Volo.Blogging.Comments +namespace Volo.Blogging { public class CommentRepository_Tests : CommentRepository_Tests { - } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs index 67c5f7ce8b..59f0be4a24 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs @@ -2,12 +2,12 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; using Volo.Abp; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Modularity; +using Volo.Blogging.EntityFrameworkCore; -namespace Volo.Blogging.EntityFrameworkCore +namespace Volo.Blogging { [DependsOn( typeof(BloggingEntityFrameworkCoreModule), @@ -49,4 +49,4 @@ namespace Volo.Blogging.EntityFrameworkCore _sqliteConnection.Dispose(); } } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs index 22dd376aaf..e23247de86 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Volo.Blogging.EntityFrameworkCore; +using Volo.Blogging.Posts; -namespace Volo.Blogging.Posts +namespace Volo.Blogging { public class PostRepository_Tests : PostRepository_Tests { - } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs index 65ebebd4ff..5de23c7665 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Volo.Blogging.EntityFrameworkCore; +using Volo.Blogging.Tagging; -namespace Volo.Blogging.Tagging +namespace Volo.Blogging { public class TagRepository_Tests : TagRepository_Tests { - } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj index c3c24c1e05..093113db66 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs index 3cf6c37080..333746f0f0 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Volo.Blogging.Blogs; +using Volo.Blogging.Blogs; -namespace Volo.Blogging.MongoDB +namespace Volo.Blogging { public class BlogRepository_Tests : BlogRepository_Tests { - } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs index 8bf936d9df..353d22d3ee 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Volo.Blogging.Comments; +using Volo.Blogging.Comments; -namespace Volo.Blogging.MongoDB +namespace Volo.Blogging { public class CommentRepository_Tests : CommentRepository_Tests { - } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs index 7ac35300ab..dc2c1cd47d 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Mongo2Go; +using Mongo2Go; using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Modularity; +using Volo.Blogging.MongoDB; -namespace Volo.Blogging.MongoDB +namespace Volo.Blogging { [DependsOn( typeof(BloggingTestBaseModule), @@ -31,4 +29,4 @@ namespace Volo.Blogging.MongoDB _mongoDbRunner.Dispose(); } } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs index b392d99a05..6fbaed0768 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Volo.Blogging.Posts; +using Volo.Blogging.Posts; -namespace Volo.Blogging.MongoDB.Tests.Volo.Blogging.Posts +namespace Volo.Blogging { public class PostRepository_Tests : PostRepository_Tests { - } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs index 0fbc8b9d10..c727ef00b3 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Volo.Blogging.Tagging; +using Volo.Blogging.Tagging; -namespace Volo.Blogging.MongoDB +namespace Volo.Blogging { public class TagRepository_Tests : TagRepository_Tests { - } -} +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Tagging/TagRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Tagging/TagRepository_Tests.cs index e1752ec200..a81c7dde1e 100644 --- a/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Tagging/TagRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.TestBase/Volo/Blogging/Tagging/TagRepository_Tests.cs @@ -65,7 +65,7 @@ namespace Volo.Blogging.Tagging tag.Id }); - var qq = await TagRepository.FindByNameAsync(BloggingTestData.Blog1Id, BloggingTestData.Tag1Name); + await TagRepository.FindByNameAsync(BloggingTestData.Blog1Id, BloggingTestData.Tag1Name); (await TagRepository.FindByNameAsync(BloggingTestData.Blog1Id, BloggingTestData.Tag1Name)).UsageCount .ShouldBe(usageCount - 1); } From 14df704c32e4a79df05487f4cdcbb82538d9cf6e Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 5 Apr 2019 16:33:54 +0300 Subject: [PATCH 066/116] Implement fully async interceptors. --- .../Authorization/AuthorizationInterceptor.cs | 2 +- .../CastleAbpInterceptorAdapter.cs | 62 ++++++++++++++----- .../CastleAbpMethodInvocationAdapter.cs | 6 +- .../DynamicProxy/SimpleAsyncInterceptor.cs | 2 +- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptor.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptor.cs index 7d5007da04..7c89bcbe11 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptor.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptor.cs @@ -35,7 +35,7 @@ namespace Volo.Abp.Authorization return; } - AsyncHelper.RunSync(() => AuthorizeAsync(invocation)); + await AuthorizeAsync(invocation); await invocation.ProceedAsync(); } diff --git a/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs b/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs index 1b1b958ff1..edb52aff86 100644 --- a/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs +++ b/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Reflection; +using System.Threading.Tasks; using Castle.DynamicProxy; using Volo.Abp.DynamicProxy; using Volo.Abp.Threading; @@ -8,6 +9,20 @@ namespace Volo.Abp.Castle.DynamicProxy public class CastleAbpInterceptorAdapter : IInterceptor where TInterceptor : IAbpInterceptor { + private static readonly MethodInfo MethodExecuteWithoutReturnValueAsync = + typeof(CastleAbpInterceptorAdapter) + .GetMethod( + nameof(ExecuteWithoutReturnValueAsync), + BindingFlags.NonPublic | BindingFlags.Instance + ); + + private static readonly MethodInfo MethodExecuteWithReturnValueAsync = + typeof(CastleAbpInterceptorAdapter) + .GetMethod( + nameof(ExecuteWithReturnValueAsync), + BindingFlags.NonPublic | BindingFlags.Instance + ); + private readonly TInterceptor _abpInterceptor; public CastleAbpInterceptorAdapter(TInterceptor abpInterceptor) @@ -17,39 +32,58 @@ namespace Volo.Abp.Castle.DynamicProxy public void Intercept(IInvocation invocation) { + var proceedInfo = invocation.CaptureProceedInfo(); + var method = invocation.MethodInvocationTarget ?? invocation.Method; if (method.IsAsync()) { - InterceptAsyncMethod(invocation); + InterceptAsyncMethod(invocation, proceedInfo); } else { - InterceptSyncMethod(invocation); + InterceptSyncMethod(invocation, proceedInfo); } } - private void InterceptAsyncMethod(IInvocation invocation) + private void InterceptSyncMethod(IInvocation invocation, IInvocationProceedInfo proceedInfo) + { + _abpInterceptor.Intercept(new CastleAbpMethodInvocationAdapter(invocation, proceedInfo)); + } + + private void InterceptAsyncMethod(IInvocation invocation, IInvocationProceedInfo proceedInfo) { if (invocation.Method.ReturnType == typeof(Task)) { - invocation.ReturnValue = _abpInterceptor.InterceptAsync(new CastleAbpMethodInvocationAdapter(invocation)); + invocation.ReturnValue = MethodExecuteWithoutReturnValueAsync + .Invoke(this, new object[] { invocation, proceedInfo }); } else { - var interceptResult = _abpInterceptor.InterceptAsync(new CastleAbpMethodInvocationAdapter(invocation)); - var actualReturnValue = invocation.ReturnValue; - invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPreActionAndPostActionAndFinallyAndGetResult( - invocation.Method.ReturnType.GenericTypeArguments[0], - () => actualReturnValue, - () => interceptResult - ); + invocation.ReturnValue = MethodExecuteWithReturnValueAsync + .MakeGenericMethod(invocation.Method.ReturnType.GenericTypeArguments[0]) + .Invoke(this, new object[] {invocation, proceedInfo}); } } - private void InterceptSyncMethod(IInvocation invocation) + private async Task ExecuteWithoutReturnValueAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo) { - _abpInterceptor.Intercept(new CastleAbpMethodInvocationAdapter(invocation)); + await Task.Yield(); + + await _abpInterceptor.InterceptAsync( + new CastleAbpMethodInvocationAdapter(invocation, proceedInfo) + ); + } + + private async Task ExecuteWithReturnValueAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo) + { + await Task.Yield(); + + await _abpInterceptor.InterceptAsync( + new CastleAbpMethodInvocationAdapter(invocation, proceedInfo) + ); + + return await (Task)invocation.ReturnValue; } } } diff --git a/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs b/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs index 5fd4270854..0963da406b 100644 --- a/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs +++ b/framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs @@ -32,10 +32,10 @@ namespace Volo.Abp.Castle.DynamicProxy protected IInvocation Invocation { get; } protected IInvocationProceedInfo ProceedInfo { get; } - public CastleAbpMethodInvocationAdapter(IInvocation invocation) + public CastleAbpMethodInvocationAdapter(IInvocation invocation, IInvocationProceedInfo proceedInfo) { Invocation = invocation; - ProceedInfo = invocation.CaptureProceedInfo(); + ProceedInfo = proceedInfo; _lazyArgumentsDictionary = new Lazy>(GetArgumentsDictionary); } @@ -53,7 +53,9 @@ namespace Volo.Abp.Castle.DynamicProxy public Task ProceedAsync() { ProceedInfo.Invoke(); + _actualReturnValue = Invocation.ReturnValue; + return Invocation.Method.IsAsync() ? (Task)_actualReturnValue : Task.FromResult(_actualReturnValue); diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs index 10a072c3c7..f1f868700a 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs @@ -14,7 +14,7 @@ namespace Volo.Abp.DynamicProxy public override async Task InterceptAsync(IAbpMethodInvocation invocation) { - //await Task.Delay(5); CAN NOT USE await before method execution! This is a restriction of Castle DynamicProxy + await Task.Delay(5); (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add($"{GetType().Name}_InterceptAsync_BeforeInvocation"); await invocation.ProceedAsync(); (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add($"{GetType().Name}_InterceptAsync_AfterInvocation"); From 8c416c2439fa16afa2a41db44ea8d6e176292888 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 5 Apr 2019 16:48:38 +0300 Subject: [PATCH 067/116] Update CachedTestObject.cs --- .../Volo/Abp/DynamicProxy/CachedTestObject.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs index 432d91bbb7..db06b78b6a 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs @@ -1,3 +1,4 @@ +using System.Threading; using System.Threading.Tasks; namespace Volo.Abp.DynamicProxy @@ -6,6 +7,7 @@ namespace Volo.Abp.DynamicProxy { public virtual int GetValue(int v) { + Thread.Sleep(5); return v; } From 4fc6a2e8d28fec015cb23da6c6031b9eb3fcdee0 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 5 Apr 2019 16:54:06 +0300 Subject: [PATCH 068/116] Re-enable async lock on distributed cache --- .../Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 75b024740c..9ec056d4d7 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -30,7 +30,7 @@ namespace Volo.Abp.Caching protected ICurrentTenant CurrentTenant { get; } - //protected AsyncLock AsyncLock { get; } = new AsyncLock(); + protected AsyncLock AsyncLock { get; } = new AsyncLock(); protected DistributedCacheEntryOptions DefaultCacheOptions; @@ -135,7 +135,7 @@ namespace Volo.Abp.Caching return value; } - //using (AsyncLock.Lock(CancellationTokenProvider.Token)) + using (AsyncLock.Lock(CancellationTokenProvider.Token)) { value = Get(key, hideErrors); if (value != null) @@ -164,7 +164,7 @@ namespace Volo.Abp.Caching return value; } - //using (await AsyncLock.LockAsync(token)) + using (await AsyncLock.LockAsync(token)) { value = await GetAsync(key, hideErrors, token); if (value != null) From dd98510ef9ca2f7b333c2a2e53ad2fe17f1c7ee0 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 5 Apr 2019 19:23:57 +0300 Subject: [PATCH 069/116] Add client-simulation solution --- .../Volo.ClientSimulation.Web.sln | 25 +++++++++++++++++ .../Volo.ClientSimulation.Web/Program.cs | 17 ++++++++++++ .../Properties/launchSettings.json | 27 +++++++++++++++++++ .../Volo.ClientSimulation.Web/Startup.cs | 27 +++++++++++++++++++ .../Volo.ClientSimulation.Web.csproj | 13 +++++++++ .../appsettings.json | 3 +++ 6 files changed, 112 insertions(+) create mode 100644 utils/client-simulation/Volo.ClientSimulation.Web.sln create mode 100644 utils/client-simulation/Volo.ClientSimulation.Web/Program.cs create mode 100644 utils/client-simulation/Volo.ClientSimulation.Web/Properties/launchSettings.json create mode 100644 utils/client-simulation/Volo.ClientSimulation.Web/Startup.cs create mode 100644 utils/client-simulation/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj create mode 100644 utils/client-simulation/Volo.ClientSimulation.Web/appsettings.json diff --git a/utils/client-simulation/Volo.ClientSimulation.Web.sln b/utils/client-simulation/Volo.ClientSimulation.Web.sln new file mode 100644 index 0000000000..9984c72f84 --- /dev/null +++ b/utils/client-simulation/Volo.ClientSimulation.Web.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.168 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.ClientSimulation.Web", "Volo.ClientSimulation.Web\Volo.ClientSimulation.Web.csproj", "{163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1AF598E1-2012-47B4-A591-ADC9F327600A} + EndGlobalSection +EndGlobal diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/Program.cs b/utils/client-simulation/Volo.ClientSimulation.Web/Program.cs new file mode 100644 index 0000000000..c1a6efa0b2 --- /dev/null +++ b/utils/client-simulation/Volo.ClientSimulation.Web/Program.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; + +namespace Volo.ClientSimulation.Web +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/Properties/launchSettings.json b/utils/client-simulation/Volo.ClientSimulation.Web/Properties/launchSettings.json new file mode 100644 index 0000000000..e64f5f1af1 --- /dev/null +++ b/utils/client-simulation/Volo.ClientSimulation.Web/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:62395", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Volo.ClientSimulation.Web": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/Startup.cs b/utils/client-simulation/Volo.ClientSimulation.Web/Startup.cs new file mode 100644 index 0000000000..20956d5505 --- /dev/null +++ b/utils/client-simulation/Volo.ClientSimulation.Web/Startup.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Volo.ClientSimulation.Web +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.Run(async (context) => + { + await context.Response.WriteAsync("Hello World!"); + }); + } + } +} diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj b/utils/client-simulation/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj new file mode 100644 index 0000000000..423afacffc --- /dev/null +++ b/utils/client-simulation/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp2.2 + InProcess + + + + + + + + diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/appsettings.json b/utils/client-simulation/Volo.ClientSimulation.Web/appsettings.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/utils/client-simulation/Volo.ClientSimulation.Web/appsettings.json @@ -0,0 +1,3 @@ +{ + +} From 3984ff065171f077cd584f7bfcb42d2409e653f8 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 5 Apr 2019 19:39:36 +0300 Subject: [PATCH 070/116] create layout and initial page. --- .../Volo.ClientSimulation.Web.sln | 15 +- .../Volo.ClientSimulation.Web/Program.cs | 17 - .../Volo.ClientSimulation.Web/Startup.cs | 27 - .../Volo.ClientSimulation.Web.csproj | 13 - .../BrandingProvider.cs | 11 + .../ClientSimulationWebModule.cs | 31 + .../Pages/Index.cshtml | 8 + .../Pages/Index.cshtml.cs | 16 + .../Pages/_ViewImports.cshtml | 4 + .../src/Volo.ClientSimulation.Web/Program.cs | 22 + .../Properties/launchSettings.json | 0 .../src/Volo.ClientSimulation.Web/Startup.cs | 25 + .../Volo.ClientSimulation.Web.csproj | 17 + .../abp.resourcemapping.js | 8 + .../appsettings.json | 0 .../src/Volo.ClientSimulation.Web/gulpfile.js | 11 + .../Volo.ClientSimulation.Web/package.json | 8 + .../wwwroot/libs/abp/core/abp.js | 546 + .../wwwroot/libs/abp/jquery/abp.jquery.js | 389 + .../wwwroot/libs/bootstrap/css/bootstrap.css | 10038 ++++++++++ .../libs/bootstrap/js/bootstrap.bundle.js | 7013 +++++++ .../css/dataTables.bootstrap4.css | 206 + .../js/dataTables.bootstrap4.js | 184 + .../datatables.net/js/jquery.dataTables.js | 15296 ++++++++++++++++ .../libs/font-awesome/css/font-awesome.css | 2337 +++ .../libs/font-awesome/fonts/FontAwesome.otf | Bin 0 -> 134808 bytes .../fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../fonts/fontawesome-webfont.svg | 2671 +++ .../fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes .../libs/jquery-form/jquery.form.min.js | 23 + .../jquery.validate.unobtrusive.js | 432 + .../libs/jquery-validation/jquery.validate.js | 1650 ++ .../localization/messages_ar.js | 35 + .../localization/messages_ar.min.js | 4 + .../localization/messages_az.js | 35 + .../localization/messages_az.min.js | 4 + .../localization/messages_bg.js | 35 + .../localization/messages_bg.min.js | 4 + .../localization/messages_bn_BD.js | 35 + .../localization/messages_bn_BD.min.js | 4 + .../localization/messages_ca.js | 35 + .../localization/messages_ca.min.js | 4 + .../localization/messages_cs.js | 36 + .../localization/messages_cs.min.js | 4 + .../localization/messages_da.js | 46 + .../localization/messages_da.min.js | 4 + .../localization/messages_de.js | 82 + .../localization/messages_de.min.js | 4 + .../localization/messages_el.js | 35 + .../localization/messages_el.min.js | 4 + .../localization/messages_es.js | 38 + .../localization/messages_es.min.js | 4 + .../localization/messages_es_AR.js | 39 + .../localization/messages_es_AR.min.js | 4 + .../localization/messages_es_PE.js | 39 + .../localization/messages_es_PE.min.js | 4 + .../localization/messages_et.js | 33 + .../localization/messages_et.min.js | 4 + .../localization/messages_eu.js | 35 + .../localization/messages_eu.min.js | 4 + .../localization/messages_fa.js | 39 + .../localization/messages_fa.min.js | 4 + .../localization/messages_fi.js | 33 + .../localization/messages_fi.min.js | 4 + .../localization/messages_fr.js | 63 + .../localization/messages_fr.min.js | 4 + .../localization/messages_ge.js | 35 + .../localization/messages_ge.min.js | 4 + .../localization/messages_gl.js | 40 + .../localization/messages_gl.min.js | 4 + .../localization/messages_he.js | 35 + .../localization/messages_he.min.js | 4 + .../localization/messages_hr.js | 35 + .../localization/messages_hr.min.js | 4 + .../localization/messages_hu.js | 35 + .../localization/messages_hu.min.js | 4 + .../localization/messages_hy_AM.js | 35 + .../localization/messages_hy_AM.min.js | 4 + .../localization/messages_id.js | 34 + .../localization/messages_id.min.js | 4 + .../localization/messages_is.js | 33 + .../localization/messages_is.min.js | 4 + .../localization/messages_it.js | 39 + .../localization/messages_it.min.js | 4 + .../localization/messages_ja.js | 36 + .../localization/messages_ja.min.js | 4 + .../localization/messages_ka.js | 35 + .../localization/messages_ka.min.js | 4 + .../localization/messages_kk.js | 35 + .../localization/messages_kk.min.js | 4 + .../localization/messages_ko.js | 35 + .../localization/messages_ko.min.js | 4 + .../localization/messages_lt.js | 35 + .../localization/messages_lt.min.js | 4 + .../localization/messages_lv.js | 35 + .../localization/messages_lv.min.js | 4 + .../localization/messages_mk.js | 35 + .../localization/messages_mk.min.js | 4 + .../localization/messages_my.js | 35 + .../localization/messages_my.min.js | 4 + .../localization/messages_nl.js | 46 + .../localization/messages_nl.min.js | 4 + .../localization/messages_no.js | 35 + .../localization/messages_no.min.js | 4 + .../localization/messages_pl.js | 38 + .../localization/messages_pl.min.js | 4 + .../localization/messages_pt_BR.js | 91 + .../localization/messages_pt_BR.min.js | 4 + .../localization/messages_pt_PT.js | 39 + .../localization/messages_pt_PT.min.js | 4 + .../localization/messages_ro.js | 35 + .../localization/messages_ro.min.js | 4 + .../localization/messages_ru.js | 35 + .../localization/messages_ru.min.js | 4 + .../localization/messages_sd.js | 35 + .../localization/messages_sd.min.js | 4 + .../localization/messages_si.js | 35 + .../localization/messages_si.min.js | 4 + .../localization/messages_sk.js | 33 + .../localization/messages_sk.min.js | 4 + .../localization/messages_sl.js | 35 + .../localization/messages_sl.min.js | 4 + .../localization/messages_sr.js | 35 + .../localization/messages_sr.min.js | 4 + .../localization/messages_sr_lat.js | 35 + .../localization/messages_sr_lat.min.js | 4 + .../localization/messages_sv.js | 35 + .../localization/messages_sv.min.js | 4 + .../localization/messages_th.js | 35 + .../localization/messages_th.min.js | 4 + .../localization/messages_tj.js | 35 + .../localization/messages_tj.min.js | 4 + .../localization/messages_tr.js | 36 + .../localization/messages_tr.min.js | 4 + .../localization/messages_uk.js | 35 + .../localization/messages_uk.min.js | 4 + .../localization/messages_ur.js | 35 + .../localization/messages_ur.min.js | 4 + .../localization/messages_vi.js | 35 + .../localization/messages_vi.min.js | 4 + .../localization/messages_zh.js | 36 + .../localization/messages_zh.min.js | 4 + .../localization/messages_zh_TW.js | 36 + .../localization/messages_zh_TW.min.js | 4 + .../localization/methods_de.js | 24 + .../localization/methods_de.min.js | 4 + .../localization/methods_es_CL.js | 24 + .../localization/methods_es_CL.min.js | 4 + .../localization/methods_fi.js | 24 + .../localization/methods_fi.min.js | 4 + .../localization/methods_it.js | 24 + .../localization/methods_it.min.js | 4 + .../localization/methods_nl.js | 24 + .../localization/methods_nl.min.js | 4 + .../localization/methods_pt.js | 21 + .../localization/methods_pt.min.js | 4 + .../wwwroot/libs/jquery/jquery.js | 10364 +++++++++++ .../wwwroot/libs/lodash/lodash.min.js | 137 + .../wwwroot/libs/select2/css/select2.min.css | 1 + .../wwwroot/libs/select2/js/i18n/af.js | 3 + .../wwwroot/libs/select2/js/i18n/ar.js | 3 + .../wwwroot/libs/select2/js/i18n/az.js | 3 + .../wwwroot/libs/select2/js/i18n/bg.js | 3 + .../wwwroot/libs/select2/js/i18n/bs.js | 3 + .../wwwroot/libs/select2/js/i18n/ca.js | 3 + .../wwwroot/libs/select2/js/i18n/cs.js | 3 + .../wwwroot/libs/select2/js/i18n/da.js | 3 + .../wwwroot/libs/select2/js/i18n/de.js | 3 + .../wwwroot/libs/select2/js/i18n/dsb.js | 3 + .../wwwroot/libs/select2/js/i18n/el.js | 3 + .../wwwroot/libs/select2/js/i18n/en.js | 3 + .../wwwroot/libs/select2/js/i18n/es.js | 3 + .../wwwroot/libs/select2/js/i18n/et.js | 3 + .../wwwroot/libs/select2/js/i18n/eu.js | 3 + .../wwwroot/libs/select2/js/i18n/fa.js | 3 + .../wwwroot/libs/select2/js/i18n/fi.js | 3 + .../wwwroot/libs/select2/js/i18n/fr.js | 3 + .../wwwroot/libs/select2/js/i18n/gl.js | 3 + .../wwwroot/libs/select2/js/i18n/he.js | 3 + .../wwwroot/libs/select2/js/i18n/hi.js | 3 + .../wwwroot/libs/select2/js/i18n/hr.js | 3 + .../wwwroot/libs/select2/js/i18n/hsb.js | 3 + .../wwwroot/libs/select2/js/i18n/hu.js | 3 + .../wwwroot/libs/select2/js/i18n/hy.js | 3 + .../wwwroot/libs/select2/js/i18n/id.js | 3 + .../wwwroot/libs/select2/js/i18n/is.js | 3 + .../wwwroot/libs/select2/js/i18n/it.js | 3 + .../wwwroot/libs/select2/js/i18n/ja.js | 3 + .../wwwroot/libs/select2/js/i18n/km.js | 3 + .../wwwroot/libs/select2/js/i18n/ko.js | 3 + .../wwwroot/libs/select2/js/i18n/lt.js | 3 + .../wwwroot/libs/select2/js/i18n/lv.js | 3 + .../wwwroot/libs/select2/js/i18n/mk.js | 3 + .../wwwroot/libs/select2/js/i18n/ms.js | 3 + .../wwwroot/libs/select2/js/i18n/nb.js | 3 + .../wwwroot/libs/select2/js/i18n/nl.js | 3 + .../wwwroot/libs/select2/js/i18n/pl.js | 3 + .../wwwroot/libs/select2/js/i18n/ps.js | 3 + .../wwwroot/libs/select2/js/i18n/pt-BR.js | 3 + .../wwwroot/libs/select2/js/i18n/pt.js | 3 + .../wwwroot/libs/select2/js/i18n/ro.js | 3 + .../wwwroot/libs/select2/js/i18n/ru.js | 3 + .../wwwroot/libs/select2/js/i18n/sk.js | 3 + .../wwwroot/libs/select2/js/i18n/sl.js | 3 + .../wwwroot/libs/select2/js/i18n/sr-Cyrl.js | 3 + .../wwwroot/libs/select2/js/i18n/sr.js | 3 + .../wwwroot/libs/select2/js/i18n/sv.js | 3 + .../wwwroot/libs/select2/js/i18n/th.js | 3 + .../wwwroot/libs/select2/js/i18n/tr.js | 3 + .../wwwroot/libs/select2/js/i18n/uk.js | 3 + .../wwwroot/libs/select2/js/i18n/vi.js | 3 + .../wwwroot/libs/select2/js/i18n/zh-CN.js | 3 + .../wwwroot/libs/select2/js/i18n/zh-TW.js | 3 + .../libs/select2/js/select2.full.min.js | 1 + .../wwwroot/libs/select2/js/select2.min.js | 1 + .../wwwroot/libs/sweetalert/sweetalert.min.js | 1 + .../wwwroot/libs/timeago/jquery.timeago.js | 232 + .../wwwroot/libs/timeago/locales/README.md | 27 + .../libs/timeago/locales/jquery.timeago.af.js | 30 + .../libs/timeago/locales/jquery.timeago.am.js | 30 + .../libs/timeago/locales/jquery.timeago.ar.js | 104 + .../locales/jquery.timeago.az-short.js | 30 + .../libs/timeago/locales/jquery.timeago.az.js | 30 + .../libs/timeago/locales/jquery.timeago.be.js | 43 + .../libs/timeago/locales/jquery.timeago.bg.js | 28 + .../libs/timeago/locales/jquery.timeago.bs.js | 55 + .../libs/timeago/locales/jquery.timeago.ca.js | 30 + .../libs/timeago/locales/jquery.timeago.cs.js | 34 + .../libs/timeago/locales/jquery.timeago.cy.js | 30 + .../libs/timeago/locales/jquery.timeago.da.js | 28 + .../locales/jquery.timeago.de-short.js | 30 + .../libs/timeago/locales/jquery.timeago.de.js | 28 + .../libs/timeago/locales/jquery.timeago.dv.js | 32 + .../libs/timeago/locales/jquery.timeago.el.js | 28 + .../locales/jquery.timeago.en-short.js | 30 + .../libs/timeago/locales/jquery.timeago.en.js | 30 + .../locales/jquery.timeago.es-short.js | 31 + .../libs/timeago/locales/jquery.timeago.es.js | 29 + .../libs/timeago/locales/jquery.timeago.et.js | 28 + .../libs/timeago/locales/jquery.timeago.eu.js | 28 + .../locales/jquery.timeago.fa-short.js | 30 + .../libs/timeago/locales/jquery.timeago.fa.js | 32 + .../libs/timeago/locales/jquery.timeago.fi.js | 38 + .../locales/jquery.timeago.fr-short.js | 26 + .../libs/timeago/locales/jquery.timeago.fr.js | 27 + .../libs/timeago/locales/jquery.timeago.gl.js | 28 + .../libs/timeago/locales/jquery.timeago.he.js | 26 + .../libs/timeago/locales/jquery.timeago.hr.js | 54 + .../libs/timeago/locales/jquery.timeago.hu.js | 28 + .../libs/timeago/locales/jquery.timeago.hy.js | 28 + .../libs/timeago/locales/jquery.timeago.id.js | 29 + .../libs/timeago/locales/jquery.timeago.is.js | 29 + .../locales/jquery.timeago.it-short.js | 30 + .../libs/timeago/locales/jquery.timeago.it.js | 28 + .../libs/timeago/locales/jquery.timeago.ja.js | 29 + .../libs/timeago/locales/jquery.timeago.jv.js | 28 + .../libs/timeago/locales/jquery.timeago.ko.js | 31 + .../libs/timeago/locales/jquery.timeago.ky.js | 42 + .../libs/timeago/locales/jquery.timeago.lt.js | 30 + .../libs/timeago/locales/jquery.timeago.lv.js | 30 + .../libs/timeago/locales/jquery.timeago.mk.js | 30 + .../libs/timeago/locales/jquery.timeago.nl.js | 30 + .../libs/timeago/locales/jquery.timeago.no.js | 28 + .../libs/timeago/locales/jquery.timeago.pl.js | 39 + .../locales/jquery.timeago.pt-br-short.js | 30 + .../timeago/locales/jquery.timeago.pt-br.js | 28 + .../locales/jquery.timeago.pt-short.js | 30 + .../libs/timeago/locales/jquery.timeago.pt.js | 26 + .../libs/timeago/locales/jquery.timeago.ro.js | 29 + .../libs/timeago/locales/jquery.timeago.rs.js | 54 + .../libs/timeago/locales/jquery.timeago.ru.js | 43 + .../libs/timeago/locales/jquery.timeago.rw.js | 30 + .../libs/timeago/locales/jquery.timeago.si.js | 28 + .../libs/timeago/locales/jquery.timeago.sk.js | 34 + .../libs/timeago/locales/jquery.timeago.sl.js | 46 + .../libs/timeago/locales/jquery.timeago.sq.js | 26 + .../libs/timeago/locales/jquery.timeago.sr.js | 54 + .../libs/timeago/locales/jquery.timeago.sv.js | 28 + .../libs/timeago/locales/jquery.timeago.th.js | 30 + .../locales/jquery.timeago.tr-short.js | 30 + .../libs/timeago/locales/jquery.timeago.tr.js | 26 + .../libs/timeago/locales/jquery.timeago.uk.js | 42 + .../libs/timeago/locales/jquery.timeago.ur.js | 30 + .../libs/timeago/locales/jquery.timeago.uz.js | 29 + .../libs/timeago/locales/jquery.timeago.vi.js | 30 + .../timeago/locales/jquery.timeago.zh-CN.js | 31 + .../timeago/locales/jquery.timeago.zh-TW.js | 30 + .../wwwroot/libs/toastr/toastr.css | 228 + .../wwwroot/libs/toastr/toastr.js.map | 1 + .../wwwroot/libs/toastr/toastr.min.css | 1 + .../wwwroot/libs/toastr/toastr.min.js | 2 + .../src/Volo.ClientSimulation.Web/yarn.lock | 1648 ++ 294 files changed, 58571 insertions(+), 62 deletions(-) delete mode 100644 utils/client-simulation/Volo.ClientSimulation.Web/Program.cs delete mode 100644 utils/client-simulation/Volo.ClientSimulation.Web/Startup.cs delete mode 100644 utils/client-simulation/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/BrandingProvider.cs create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/ClientSimulationWebModule.cs create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml.cs create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/_ViewImports.cshtml create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/Program.cs rename utils/client-simulation/{ => src}/Volo.ClientSimulation.Web/Properties/launchSettings.json (100%) create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/Startup.cs create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/abp.resourcemapping.js rename utils/client-simulation/{ => src}/Volo.ClientSimulation.Web/appsettings.json (100%) create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/gulpfile.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/package.json create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/abp/core/abp.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/abp/jquery/abp.jquery.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/bootstrap/css/bootstrap.css create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/bootstrap/js/bootstrap.bundle.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/datatables.net-bs4/css/dataTables.bootstrap4.css create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/datatables.net-bs4/js/dataTables.bootstrap4.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/datatables.net/js/jquery.dataTables.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/font-awesome/css/font-awesome.css create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/font-awesome/fonts/FontAwesome.otf create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.eot create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.svg create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.ttf create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff2 create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-form/jquery.form.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/jquery.validate.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ar.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ar.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_az.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_az.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_bg.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_bg.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_bn_BD.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_bn_BD.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ca.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ca.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_cs.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_cs.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_da.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_da.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_de.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_de.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_el.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_el.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_es.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_es.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_es_AR.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_es_AR.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_es_PE.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_es_PE.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_et.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_et.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_eu.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_eu.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_fa.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_fa.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_fi.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_fi.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_fr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_fr.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ge.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ge.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_gl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_gl.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_he.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_he.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_hr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_hr.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_hu.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_hu.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_hy_AM.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_hy_AM.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_id.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_id.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_is.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_is.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_it.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_it.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ja.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ja.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ka.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ka.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_kk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_kk.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ko.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ko.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_lt.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_lt.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_lv.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_lv.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_mk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_mk.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_my.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_my.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_nl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_nl.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_no.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_no.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_pl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_pl.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_pt_BR.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_pt_BR.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_pt_PT.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_pt_PT.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ro.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ro.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ru.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ru.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sd.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sd.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_si.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_si.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sk.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sl.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sr.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sr_lat.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sr_lat.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sv.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_sv.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_th.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_th.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_tj.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_tj.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_tr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_tr.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_uk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_uk.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ur.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_ur.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_vi.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_vi.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_zh.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_zh.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_zh_TW.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/messages_zh_TW.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_de.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_de.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_es_CL.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_es_CL.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_fi.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_fi.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_it.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_it.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_nl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_nl.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_pt.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery-validation/localization/methods_pt.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/jquery/jquery.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/lodash/lodash.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/css/select2.min.css create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/af.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/ar.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/az.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/bg.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/bs.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/ca.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/cs.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/da.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/de.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/dsb.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/el.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/en.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/es.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/et.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/eu.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/fa.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/fi.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/fr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/gl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/he.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/hi.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/hr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/hsb.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/hu.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/hy.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/id.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/is.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/it.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/ja.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/km.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/ko.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/lt.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/lv.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/mk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/ms.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/nb.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/nl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/pl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/ps.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/pt-BR.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/pt.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/ro.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/ru.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/sk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/sl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/sr-Cyrl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/sr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/sv.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/th.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/tr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/uk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/vi.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/zh-CN.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/i18n/zh-TW.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/select2.full.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/select2/js/select2.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/sweetalert/sweetalert.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/jquery.timeago.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/README.md create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.af.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.am.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.ar.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.az-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.az.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.be.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.bg.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.bs.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.ca.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.cs.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.cy.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.da.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.de-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.de.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.dv.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.el.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.en-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.en.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.es-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.es.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.et.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.eu.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.fa-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.fa.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.fi.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.fr-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.fr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.gl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.he.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.hr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.hu.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.hy.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.id.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.is.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.it-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.it.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.ja.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.jv.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.ko.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.ky.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.lt.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.lv.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.mk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.nl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.no.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.pl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-br-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-br.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.ro.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.rs.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.ru.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.rw.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.si.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.sk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.sl.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.sq.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.sr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.sv.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.th.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.tr-short.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.tr.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.uk.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.ur.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.uz.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.vi.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.zh-CN.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/timeago/locales/jquery.timeago.zh-TW.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/toastr/toastr.css create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/toastr/toastr.js.map create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/toastr/toastr.min.css create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/toastr/toastr.min.js create mode 100644 utils/client-simulation/src/Volo.ClientSimulation.Web/yarn.lock diff --git a/utils/client-simulation/Volo.ClientSimulation.Web.sln b/utils/client-simulation/Volo.ClientSimulation.Web.sln index 9984c72f84..a88d910396 100644 --- a/utils/client-simulation/Volo.ClientSimulation.Web.sln +++ b/utils/client-simulation/Volo.ClientSimulation.Web.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.ClientSimulation.Web", "Volo.ClientSimulation.Web\Volo.ClientSimulation.Web.csproj", "{163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{42503D63-D292-4A18-8ECE-8270167DD842}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.ClientSimulation.Web", "src\Volo.ClientSimulation.Web\Volo.ClientSimulation.Web.csproj", "{7579B444-3411-4E3B-8DF3-19684C1F34C2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,14 +13,17 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}.Debug|Any CPU.Build.0 = Debug|Any CPU - {163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}.Release|Any CPU.ActiveCfg = Release|Any CPU - {163794E7-F5EE-4AA7-8EA7-6F3BC7F11A83}.Release|Any CPU.Build.0 = Release|Any CPU + {7579B444-3411-4E3B-8DF3-19684C1F34C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7579B444-3411-4E3B-8DF3-19684C1F34C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7579B444-3411-4E3B-8DF3-19684C1F34C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7579B444-3411-4E3B-8DF3-19684C1F34C2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {7579B444-3411-4E3B-8DF3-19684C1F34C2} = {42503D63-D292-4A18-8ECE-8270167DD842} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1AF598E1-2012-47B4-A591-ADC9F327600A} EndGlobalSection diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/Program.cs b/utils/client-simulation/Volo.ClientSimulation.Web/Program.cs deleted file mode 100644 index c1a6efa0b2..0000000000 --- a/utils/client-simulation/Volo.ClientSimulation.Web/Program.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; - -namespace Volo.ClientSimulation.Web -{ - public class Program - { - public static void Main(string[] args) - { - CreateWebHostBuilder(args).Build().Run(); - } - - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup(); - } -} diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/Startup.cs b/utils/client-simulation/Volo.ClientSimulation.Web/Startup.cs deleted file mode 100644 index 20956d5505..0000000000 --- a/utils/client-simulation/Volo.ClientSimulation.Web/Startup.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; - -namespace Volo.ClientSimulation.Web -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - } - - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.Run(async (context) => - { - await context.Response.WriteAsync("Hello World!"); - }); - } - } -} diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj b/utils/client-simulation/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj deleted file mode 100644 index 423afacffc..0000000000 --- a/utils/client-simulation/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - netcoreapp2.2 - InProcess - - - - - - - - diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/BrandingProvider.cs b/utils/client-simulation/src/Volo.ClientSimulation.Web/BrandingProvider.cs new file mode 100644 index 0000000000..6b6c5f85e2 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/BrandingProvider.cs @@ -0,0 +1,11 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; +using Volo.Abp.DependencyInjection; + +namespace Volo.ClientSimulation.Web +{ + [Dependency(ReplaceServices = true)] + public class BrandingProvider : DefaultBrandingProvider + { + public override string AppName => "Client Simulation"; + } +} diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/ClientSimulationWebModule.cs b/utils/client-simulation/src/Volo.ClientSimulation.Web/ClientSimulationWebModule.cs new file mode 100644 index 0000000000..b2a44588ea --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/ClientSimulationWebModule.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.ClientSimulation.Web +{ + [DependsOn( + typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(AbpAutofacModule) + )] + public class ClientSimulationWebModule : AbpModule + { + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + var env = context.GetEnvironment(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseVirtualFiles(); + + app.UseMvcWithDefaultRoute(); + } + } +} diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml new file mode 100644 index 0000000000..868d03b922 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml @@ -0,0 +1,8 @@ +@page +@model Volo.ClientSimulation.Web.Pages.IndexModel +@{ +} +
+ Start + Stop +
\ No newline at end of file diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml.cs b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml.cs new file mode 100644 index 0000000000..b8f1446ab4 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Volo.ClientSimulation.Web.Pages +{ + public class IndexModel : PageModel + { + public void OnGet() + { + } + } +} \ No newline at end of file diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/_ViewImports.cshtml b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/_ViewImports.cshtml new file mode 100644 index 0000000000..c1da1f5f10 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/_ViewImports.cshtml @@ -0,0 +1,4 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling \ No newline at end of file diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Program.cs b/utils/client-simulation/src/Volo.ClientSimulation.Web/Program.cs new file mode 100644 index 0000000000..034a01377e --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Program.cs @@ -0,0 +1,22 @@ +using System.IO; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; + +namespace Volo.ClientSimulation.Web +{ + public class Program + { + public static void Main(string[] args) + { + BuildWebHostInternal(args).Run(); + } + + public static IWebHost BuildWebHostInternal(string[] args) => + new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + } +} diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/Properties/launchSettings.json b/utils/client-simulation/src/Volo.ClientSimulation.Web/Properties/launchSettings.json similarity index 100% rename from utils/client-simulation/Volo.ClientSimulation.Web/Properties/launchSettings.json rename to utils/client-simulation/src/Volo.ClientSimulation.Web/Properties/launchSettings.json diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Startup.cs b/utils/client-simulation/src/Volo.ClientSimulation.Web/Startup.cs new file mode 100644 index 0000000000..cafa25d3b1 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Startup.cs @@ -0,0 +1,25 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; + +namespace Volo.ClientSimulation.Web +{ + public class Startup + { + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddApplication(options => + { + options.UseAutofac(); + }); + + return services.BuildServiceProviderFromFactory(); + } + + public void Configure(IApplicationBuilder app) + { + app.InitializeApplication(); + } + } +} diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj b/utils/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj new file mode 100644 index 0000000000..c38936b20d --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/abp.resourcemapping.js b/utils/client-simulation/src/Volo.ClientSimulation.Web/abp.resourcemapping.js new file mode 100644 index 0000000000..96f7c92778 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/abp.resourcemapping.js @@ -0,0 +1,8 @@ +module.exports = { + aliases: { + + }, + mappings: { + + } +} \ No newline at end of file diff --git a/utils/client-simulation/Volo.ClientSimulation.Web/appsettings.json b/utils/client-simulation/src/Volo.ClientSimulation.Web/appsettings.json similarity index 100% rename from utils/client-simulation/Volo.ClientSimulation.Web/appsettings.json rename to utils/client-simulation/src/Volo.ClientSimulation.Web/appsettings.json diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/gulpfile.js b/utils/client-simulation/src/Volo.ClientSimulation.Web/gulpfile.js new file mode 100644 index 0000000000..db4c6fd03c --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/gulpfile.js @@ -0,0 +1,11 @@ +"use strict"; + +var gulp = require("gulp"), + path = require('path'), + copyResources = require('./node_modules/@abp/aspnetcore.mvc.ui/gulp/copy-resources.js'); + +copyResources.init(path.resolve('./')); + +gulp.task('default', [copyResources.taskName], function () { + +}); \ No newline at end of file diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/package.json b/utils/client-simulation/src/Volo.ClientSimulation.Web/package.json new file mode 100644 index 0000000000..1d433583f8 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/package.json @@ -0,0 +1,8 @@ +{ + "version": "0.1.0", + "name": "client-simulation-web", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^0.4.9" + } +} diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/abp/core/abp.js b/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/abp/core/abp.js new file mode 100644 index 0000000000..4c87489e22 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/abp/core/abp.js @@ -0,0 +1,546 @@ +var abp = abp || {}; +(function () { + + /* Application paths *****************************************/ + + //Current application root path (including virtual directory if exists). + abp.appPath = abp.appPath || '/'; + + abp.pageLoadTime = new Date(); + + //Converts given path to absolute path using abp.appPath variable. + abp.toAbsAppPath = function (path) { + if (path.indexOf('/') == 0) { + path = path.substring(1); + } + + return abp.appPath + path; + }; + + /* LOGGING ***************************************************/ + //Implements Logging API that provides secure & controlled usage of console.log + + abp.log = abp.log || {}; + + abp.log.levels = { + DEBUG: 1, + INFO: 2, + WARN: 3, + ERROR: 4, + FATAL: 5 + }; + + abp.log.level = abp.log.levels.DEBUG; + + abp.log.log = function (logObject, logLevel) { + if (!window.console || !window.console.log) { + return; + } + + if (logLevel != undefined && logLevel < abp.log.level) { + return; + } + + console.log(logObject); + }; + + abp.log.debug = function (logObject) { + abp.log.log("DEBUG: ", abp.log.levels.DEBUG); + abp.log.log(logObject, abp.log.levels.DEBUG); + }; + + abp.log.info = function (logObject) { + abp.log.log("INFO: ", abp.log.levels.INFO); + abp.log.log(logObject, abp.log.levels.INFO); + }; + + abp.log.warn = function (logObject) { + abp.log.log("WARN: ", abp.log.levels.WARN); + abp.log.log(logObject, abp.log.levels.WARN); + }; + + abp.log.error = function (logObject) { + abp.log.log("ERROR: ", abp.log.levels.ERROR); + abp.log.log(logObject, abp.log.levels.ERROR); + }; + + abp.log.fatal = function (logObject) { + abp.log.log("FATAL: ", abp.log.levels.FATAL); + abp.log.log(logObject, abp.log.levels.FATAL); + }; + + /* LOCALIZATION ***********************************************/ + + abp.localization = abp.localization || {}; + + abp.localization.values = {}; + + abp.localization.localize = function (key, sourceName) { + sourceName = sourceName || abp.localization.defaultResourceName; + + var source = abp.localization.values[sourceName]; + + if (!source) { + abp.log.warn('Could not find localization source: ' + sourceName); + return key; + } + + var value = source[key]; + if (value == undefined) { + return key; + } + + var copiedArguments = Array.prototype.slice.call(arguments, 0); + copiedArguments.splice(1, 1); + copiedArguments[0] = value; + + return abp.utils.formatString.apply(this, copiedArguments); + }; + + abp.localization.getResource = function (name) { + return function () { + var copiedArguments = Array.prototype.slice.call(arguments, 0); + copiedArguments.splice(1, 0, name); + return abp.localization.localize.apply(this, copiedArguments); + }; + }; + + abp.localization.defaultResourceName = undefined; + + /* AUTHORIZATION **********************************************/ + + abp.auth = abp.auth || {}; + + abp.auth.policies = abp.auth.policies || {}; + + abp.auth.grantedPolicies = abp.auth.grantedPolicies || {}; + + abp.auth.isGranted = function (policyName) { + return abp.auth.policies[policyName] != undefined && abp.auth.grantedPolicies[policyName] != undefined; + }; + + abp.auth.isAnyGranted = function () { + if (!arguments || arguments.length <= 0) { + return true; + } + + for (var i = 0; i < arguments.length; i++) { + if (abp.auth.isGranted(arguments[i])) { + return true; + } + } + + return false; + }; + + abp.auth.areAllGranted = function () { + if (!arguments || arguments.length <= 0) { + return true; + } + + for (var i = 0; i < arguments.length; i++) { + if (!abp.auth.isGranted(arguments[i])) { + return false; + } + } + + return true; + }; + + abp.auth.tokenCookieName = 'Abp.AuthToken'; + + abp.auth.setToken = function (authToken, expireDate) { + abp.utils.setCookieValue(abp.auth.tokenCookieName, authToken, expireDate, abp.appPath, abp.domain); + }; + + abp.auth.getToken = function () { + return abp.utils.getCookieValue(abp.auth.tokenCookieName); + } + + abp.auth.clearToken = function () { + abp.auth.setToken(); + } + + /* NOTIFICATION *********************************************/ + //Defines Notification API, not implements it + + abp.notify = abp.notify || {}; + + abp.notify.success = function (message, title, options) { + abp.log.warn('abp.notify.success is not implemented!'); + }; + + abp.notify.info = function (message, title, options) { + abp.log.warn('abp.notify.info is not implemented!'); + }; + + abp.notify.warn = function (message, title, options) { + abp.log.warn('abp.notify.warn is not implemented!'); + }; + + abp.notify.error = function (message, title, options) { + abp.log.warn('abp.notify.error is not implemented!'); + }; + + /* MESSAGE **************************************************/ + //Defines Message API, not implements it + + abp.message = abp.message || {}; + + abp.message._showMessage = function (message, title) { + alert((title || '') + ' ' + message); + }; + + abp.message.info = function (message, title) { + abp.log.warn('abp.message.info is not implemented!'); + return abp.message._showMessage(message, title); + }; + + abp.message.success = function (message, title) { + abp.log.warn('abp.message.success is not implemented!'); + return abp.message._showMessage(message, title); + }; + + abp.message.warn = function (message, title) { + abp.log.warn('abp.message.warn is not implemented!'); + return abp.message._showMessage(message, title); + }; + + abp.message.error = function (message, title) { + abp.log.warn('abp.message.error is not implemented!'); + return abp.message._showMessage(message, title); + }; + + abp.message.confirm = function (message, titleOrCallback, callback) { + abp.log.warn('abp.message.confirm is not properly implemented!'); + + if (titleOrCallback && !(typeof titleOrCallback == 'string')) { + callback = titleOrCallback; + } + + var result = confirm(message); + callback && callback(result); + }; + + /* UI *******************************************************/ + + abp.ui = abp.ui || {}; + + /* UI BLOCK */ + //Defines UI Block API, not implements it + + abp.ui.block = function (elm) { + abp.log.warn('abp.ui.block is not implemented!'); + }; + + abp.ui.unblock = function (elm) { + abp.log.warn('abp.ui.unblock is not implemented!'); + }; + + /* UI BUSY */ + //Defines UI Busy API, not implements it + + abp.ui.setBusy = function (elm, optionsOrPromise) { + abp.log.warn('abp.ui.setBusy is not implemented!'); + }; + + abp.ui.clearBusy = function (elm) { + abp.log.warn('abp.ui.clearBusy is not implemented!'); + }; + + /* SIMPLE EVENT BUS *****************************************/ + + abp.event = (function () { + + var _callbacks = {}; + + var on = function (eventName, callback) { + if (!_callbacks[eventName]) { + _callbacks[eventName] = []; + } + + _callbacks[eventName].push(callback); + }; + + var off = function (eventName, callback) { + var callbacks = _callbacks[eventName]; + if (!callbacks) { + return; + } + + var index = -1; + for (var i = 0; i < callbacks.length; i++) { + if (callbacks[i] === callback) { + index = i; + break; + } + } + + if (index < 0) { + return; + } + + _callbacks[eventName].splice(index, 1); + }; + + var trigger = function (eventName) { + var callbacks = _callbacks[eventName]; + if (!callbacks || !callbacks.length) { + return; + } + + var args = Array.prototype.slice.call(arguments, 1); + for (var i = 0; i < callbacks.length; i++) { + callbacks[i].apply(this, args); + } + }; + + // Public interface /////////////////////////////////////////////////// + + return { + on: on, + off: off, + trigger: trigger + }; + })(); + + + /* UTILS ***************************************************/ + + abp.utils = abp.utils || {}; + + /* Creates a name namespace. + * Example: + * var taskService = abp.utils.createNamespace(abp, 'services.task'); + * taskService will be equal to abp.services.task + * first argument (root) must be defined first + ************************************************************/ + abp.utils.createNamespace = function (root, ns) { + var parts = ns.split('.'); + for (var i = 0; i < parts.length; i++) { + if (typeof root[parts[i]] == 'undefined') { + root[parts[i]] = {}; + } + + root = root[parts[i]]; + } + + return root; + }; + + /* Find and replaces a string (search) to another string (replacement) in + * given string (str). + * Example: + * abp.utils.replaceAll('This is a test string', 'is', 'X') = 'ThX X a test string' + ************************************************************/ + abp.utils.replaceAll = function (str, search, replacement) { + var fix = search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return str.replace(new RegExp(fix, 'g'), replacement); + }; + + /* Formats a string just like string.format in C#. + * Example: + * abp.utils.formatString('Hello {0}','Tuana') = 'Hello Tuana' + ************************************************************/ + abp.utils.formatString = function () { + if (arguments.length < 1) { + return null; + } + + var str = arguments[0]; + + for (var i = 1; i < arguments.length; i++) { + var placeHolder = '{' + (i - 1) + '}'; + str = abp.utils.replaceAll(str, placeHolder, arguments[i]); + } + + return str; + }; + + abp.utils.toPascalCase = function (str) { + if (!str || !str.length) { + return str; + } + + if (str.length === 1) { + return str.charAt(0).toUpperCase(); + } + + return str.charAt(0).toUpperCase() + str.substr(1); + } + + abp.utils.toCamelCase = function (str) { + if (!str || !str.length) { + return str; + } + + if (str.length === 1) { + return str.charAt(0).toLowerCase(); + } + + return str.charAt(0).toLowerCase() + str.substr(1); + } + + abp.utils.truncateString = function (str, maxLength) { + if (!str || !str.length || str.length <= maxLength) { + return str; + } + + return str.substr(0, maxLength); + }; + + abp.utils.truncateStringWithPostfix = function (str, maxLength, postfix) { + postfix = postfix || '...'; + + if (!str || !str.length || str.length <= maxLength) { + return str; + } + + if (maxLength <= postfix.length) { + return postfix.substr(0, maxLength); + } + + return str.substr(0, maxLength - postfix.length) + postfix; + }; + + abp.utils.isFunction = function (obj) { + return !!(obj && obj.constructor && obj.call && obj.apply); + }; + + /** + * parameterInfos should be an array of { name, value } objects + * where name is query string parameter name and value is it's value. + * includeQuestionMark is true by default. + */ + abp.utils.buildQueryString = function (parameterInfos, includeQuestionMark) { + if (includeQuestionMark === undefined) { + includeQuestionMark = true; + } + + var qs = ''; + + function addSeperator() { + if (!qs.length) { + if (includeQuestionMark) { + qs = qs + '?'; + } + } else { + qs = qs + '&'; + } + } + + for (var i = 0; i < parameterInfos.length; ++i) { + var parameterInfo = parameterInfos[i]; + if (parameterInfo.value === undefined) { + continue; + } + + if (parameterInfo.value === null) { + parameterInfo.value = ''; + } + + addSeperator(); + + if (parameterInfo.value.toJSON && typeof parameterInfo.value.toJSON === "function") { + qs = qs + parameterInfo.name + '=' + encodeURIComponent(parameterInfo.value.toJSON()); + } else if (Array.isArray(parameterInfo.value) && parameterInfo.value.length) { + for (var j = 0; j < parameterInfo.value.length; j++) { + if (j > 0) { + addSeperator(); + } + + qs = qs + parameterInfo.name + '[' + j + ']=' + encodeURIComponent(parameterInfo.value[j]); + } + } else { + qs = qs + parameterInfo.name + '=' + encodeURIComponent(parameterInfo.value); + } + } + + return qs; + } + + /** + * Sets a cookie value for given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @param {string} value + * @param {Date} expireDate (optional). If not specified the cookie will expire at the end of session. + * @param {string} path (optional) + */ + abp.utils.setCookieValue = function (key, value, expireDate, path) { + var cookieValue = encodeURIComponent(key) + '='; + + if (value) { + cookieValue = cookieValue + encodeURIComponent(value); + } + + if (expireDate) { + cookieValue = cookieValue + "; expires=" + expireDate.toUTCString(); + } + + if (path) { + cookieValue = cookieValue + "; path=" + path; + } + + document.cookie = cookieValue; + }; + + /** + * Gets a cookie with given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @returns {string} Cookie value or null + */ + abp.utils.getCookieValue = function (key) { + var equalities = document.cookie.split('; '); + for (var i = 0; i < equalities.length; i++) { + if (!equalities[i]) { + continue; + } + + var splitted = equalities[i].split('='); + if (splitted.length != 2) { + continue; + } + + if (decodeURIComponent(splitted[0]) === key) { + return decodeURIComponent(splitted[1] || ''); + } + } + + return null; + }; + + /** + * Deletes cookie for given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @param {string} path (optional) + */ + abp.utils.deleteCookie = function (key, path) { + var cookieValue = encodeURIComponent(key) + '='; + + cookieValue = cookieValue + "; expires=" + (new Date(new Date().getTime() - 86400000)).toUTCString(); + + if (path) { + cookieValue = cookieValue + "; path=" + path; + } + + document.cookie = cookieValue; + } + + /* SECURITY ***************************************/ + abp.security = abp.security || {}; + abp.security.antiForgery = abp.security.antiForgery || {}; + + abp.security.antiForgery.tokenCookieName = 'XSRF-TOKEN'; + abp.security.antiForgery.tokenHeaderName = 'X-XSRF-TOKEN'; + + abp.security.antiForgery.getToken = function () { + return abp.utils.getCookieValue(abp.security.antiForgery.tokenCookieName); + }; + +})(); \ No newline at end of file diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/abp/jquery/abp.jquery.js b/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/abp/jquery/abp.jquery.js new file mode 100644 index 0000000000..6a84bfbb61 --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/abp/jquery/abp.jquery.js @@ -0,0 +1,389 @@ +var abp = abp || {}; +(function($) { + + if (!$) { + throw "abp/jquery library requires the jquery library included to the page!"; + } + + // ABP CORE OVERRIDES ///////////////////////////////////////////////////// + + abp.message._showMessage = function (message, title) { + alert((title || '') + ' ' + message); + + return $.Deferred(function ($dfd) { + $dfd.resolve(); + }); + }; + + abp.message.confirm = function (message, titleOrCallback, callback) { + if (titleOrCallback && !(typeof titleOrCallback == 'string')) { + callback = titleOrCallback; + } + + var result = confirm(message); + callback && callback(result); + + return $.Deferred(function ($dfd) { + $dfd.resolve(result); + }); + }; + + abp.utils.isFunction = function (obj) { + return $.isFunction(obj); + }; + + // JQUERY EXTENSIONS ////////////////////////////////////////////////////// + + $.fn.findWithSelf = function (selector) { + return this.filter(selector).add(this.find(selector)); + }; + + // DOM //////////////////////////////////////////////////////////////////// + + abp.dom = abp.dom || {}; + + abp.dom.onNodeAdded = function (callback) { + abp.event.on('abp.dom.nodeAdded', callback); + }; + + abp.dom.onNodeRemoved = function (callback) { + abp.event.on('abp.dom.nodeRemoved', callback); + }; + + var mutationObserverCallback = function (mutationsList) { + for (var i = 0; i < mutationsList.length; i++) { + var mutation = mutationsList[i]; + if (mutation.type === 'childList') { + if (mutation.addedNodes && mutation.removedNodes.length) { + for (var k = 0; k < mutation.removedNodes.length; k++) { + abp.event.trigger( + 'abp.dom.nodeRemoved', + { + $el: $(mutation.removedNodes[k]) + } + ); + } + } + + if (mutation.addedNodes && mutation.addedNodes.length) { + for (var j = 0; j < mutation.addedNodes.length; j++) { + abp.event.trigger( + 'abp.dom.nodeAdded', + { + $el: $(mutation.addedNodes[j]) + } + ); + } + } + } + } + }; + + new MutationObserver(mutationObserverCallback).observe( + $('body')[0], + { + subtree: true, + childList: true + } + ); + + // AJAX /////////////////////////////////////////////////////////////////// + + abp.ajax = function (userOptions) { + userOptions = userOptions || {}; + + var options = $.extend(true, {}, abp.ajax.defaultOpts, userOptions); + + options.success = undefined; + options.error = undefined; + + return $.Deferred(function ($dfd) { + $.ajax(options) + .done(function (data, textStatus, jqXHR) { + $dfd.resolve(data); + userOptions.success && userOptions.success(data); + }).fail(function (jqXHR) { + if (jqXHR.getResponseHeader('_AbpErrorFormat') === 'true') { + abp.ajax.handleAbpErrorResponse(jqXHR, userOptions, $dfd); + } else { + abp.ajax.handleNonAbpErrorResponse(jqXHR, userOptions, $dfd); + } + }); + }); + }; + + $.extend(abp.ajax, { + defaultOpts: { + dataType: 'json', + type: 'POST', + contentType: 'application/json', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + } + }, + + defaultError: { + message: 'An error has occurred!', + details: 'Error detail not sent by server.' + }, + + defaultError401: { + message: 'You are not authenticated!', + details: 'You should be authenticated (sign in) in order to perform this operation.' + }, + + defaultError403: { + message: 'You are not authorized!', + details: 'You are not allowed to perform this operation.' + }, + + defaultError404: { + message: 'Resource not found!', + details: 'The resource requested could not found on the server.' + }, + + logError: function (error) { + abp.log.error(error); + }, + + showError: function (error) { + if (error.details) { + return abp.message.error(error.details, error.message); + } else { + return abp.message.error(error.message || abp.ajax.defaultError.message); + } + }, + + handleTargetUrl: function (targetUrl) { + if (!targetUrl) { + location.href = abp.appPath; + } else { + location.href = targetUrl; + } + }, + + handleErrorStatusCode: function (status) { + switch (status) { + case 401: + abp.ajax.handleUnAuthorizedRequest( + abp.ajax.showError(abp.ajax.defaultError401), + abp.appPath + ); + break; + case 403: + abp.ajax.showError(abp.ajax.defaultError403); + break; + case 404: + abp.ajax.showError(abp.ajax.defaultError404); + break; + default: + abp.ajax.showError(abp.ajax.defaultError); + break; + } + }, + + handleNonAbpErrorResponse: function (jqXHR, userOptions, $dfd) { + if (userOptions.abpHandleError !== false) { + abp.ajax.handleErrorStatusCode(jqXHR.status); + } + + $dfd.reject.apply(this, arguments); + userOptions.error && userOptions.error.apply(this, arguments); + }, + + handleAbpErrorResponse: function (jqXHR, userOptions, $dfd) { + var messagePromise = null; + + if (userOptions.abpHandleError !== false) { + messagePromise = abp.ajax.showError(jqXHR.responseJSON.error); + } + + abp.ajax.logError(jqXHR.responseJSON.error); + + $dfd && $dfd.reject(jqXHR.responseJSON.error, jqXHR); + userOptions.error && userOptions.error(jqXHR.responseJSON.error, jqXHR); + + if (jqXHR.status === 401 && userOptions.abpHandleError !== false) { + abp.ajax.handleUnAuthorizedRequest(messagePromise); + } + }, + + handleUnAuthorizedRequest: function (messagePromise, targetUrl) { + if (messagePromise) { + messagePromise.done(function () { + abp.ajax.handleTargetUrl(targetUrl); + }); + } else { + abp.ajax.handleTargetUrl(targetUrl); + } + }, + + blockUI: function (options) { + if (options.blockUI) { + if (options.blockUI === true) { //block whole page + abp.ui.setBusy(); + } else { //block an element + abp.ui.setBusy(options.blockUI); + } + } + }, + + unblockUI: function (options) { + if (options.blockUI) { + if (options.blockUI === true) { //unblock whole page + abp.ui.clearBusy(); + } else { //unblock an element + abp.ui.clearBusy(options.blockUI); + } + } + }, + + ajaxSendHandler: function (event, request, settings) { + var token = abp.security.antiForgery.getToken(); + if (!token) { + return; + } + + if (!settings.headers || settings.headers[abp.security.antiForgery.tokenHeaderName] === undefined) { + request.setRequestHeader(abp.security.antiForgery.tokenHeaderName, token); + } + } + }); + + $(document).ajaxSend(function (event, request, settings) { + return abp.ajax.ajaxSendHandler(event, request, settings); + }); + + abp.event.on('abp.configurationInitialized', function () { + var l = abp.localization.getResource('AbpUi'); + + abp.ajax.defaultError.message = l('DefaultErrorMessage'); + abp.ajax.defaultError.details = l('DefaultErrorMessageDetail'); + abp.ajax.defaultError401.message = l('DefaultErrorMessage401'); + abp.ajax.defaultError401.details = l('DefaultErrorMessage401Detail'); + abp.ajax.defaultError403.message = l('DefaultErrorMessage403'); + abp.ajax.defaultError403.details = l('DefaultErrorMessage403Detail'); + abp.ajax.defaultError404.message = l('DefaultErrorMessage404'); + abp.ajax.defaultError404.details = l('DefaultErrorMessage404Detail'); + }); + + // RESOURCE LOADER //////////////////////////////////////////////////////// + + /* UrlStates enum */ + var UrlStates = { + LOADING: 'LOADING', + LOADED: 'LOADED', + FAILED: 'FAILED' + }; + + /* UrlInfo class */ + function UrlInfo(url) { + this.url = url; + this.state = UrlStates.LOADING; + this.loadCallbacks = []; + this.failCallbacks = []; + } + + UrlInfo.prototype.succeed = function () { + this.state = UrlStates.LOADED; + for (var i = 0; i < this.loadCallbacks.length; i++) { + this.loadCallbacks[i](); + } + }; + + UrlInfo.prototype.failed = function () { + this.state = UrlStates.FAILED; + for (var i = 0; i < this.failCallbacks.length; i++) { + this.failCallbacks[i](); + } + }; + + UrlInfo.prototype.handleCallbacks = function (loadCallback, failCallback) { + switch (this.state) { + case UrlStates.LOADED: + loadCallback && loadCallback(); + break; + case UrlStates.FAILED: + failCallback && failCallback(); + break; + case UrlStates.LOADING: + this.addCallbacks(loadCallback, failCallback); + break; + } + }; + + UrlInfo.prototype.addCallbacks = function (loadCallback, failCallback) { + loadCallback && this.loadCallbacks.push(loadCallback); + failCallback && this.failCallbacks.push(failCallback); + }; + + /* ResourceLoader API */ + + abp.ResourceLoader = (function () { + + var _urlInfos = {}; + + function getCacheKey(url) { + return url; + } + + function appendTimeToUrl(url) { + + if (url.indexOf('?') < 0) { + url += '?'; + } else { + url += '&'; + } + + url += '_=' + new Date().getTime(); + + return url; + } + + var _loadFromUrl = function (url, loadCallback, failCallback, serverLoader) { + + var cacheKey = getCacheKey(url); + + var urlInfo = _urlInfos[cacheKey]; + + if (urlInfo) { + urlInfo.handleCallbacks(loadCallback, failCallback); + return; + } + + _urlInfos[cacheKey] = urlInfo = new UrlInfo(url); + urlInfo.addCallbacks(loadCallback, failCallback); + + serverLoader(urlInfo); + }; + + var _loadScript = function (url, loadCallback, failCallback) { + _loadFromUrl(url, loadCallback, failCallback, function (urlInfo) { + $.getScript(url) + .done(function () { + urlInfo.succeed(); + }) + .fail(function () { + urlInfo.failed(); + }); + }); + }; + + var _loadStyle = function (url) { + _loadFromUrl(url, undefined, undefined, function (urlInfo) { + + $('', { + rel: 'stylesheet', + type: 'text/css', + href: appendTimeToUrl(url) + }).appendTo('head'); + }); + }; + + return { + loadScript: _loadScript, + loadStyle: _loadStyle + } + })(); + +})(jQuery); \ No newline at end of file diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/bootstrap/css/bootstrap.css b/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/bootstrap/css/bootstrap.css new file mode 100644 index 0000000000..8f4758923a --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/bootstrap/css/bootstrap.css @@ -0,0 +1,10038 @@ +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +:root { + --blue: #007bff; + --indigo: #6610f2; + --purple: #6f42c1; + --pink: #e83e8c; + --red: #dc3545; + --orange: #fd7e14; + --yellow: #ffc107; + --green: #28a745; + --teal: #20c997; + --cyan: #17a2b8; + --white: #fff; + --gray: #6c757d; + --gray-dark: #343a40; + --primary: #007bff; + --secondary: #6c757d; + --success: #28a745; + --info: #17a2b8; + --warning: #ffc107; + --danger: #dc3545; + --light: #f8f9fa; + --dark: #343a40; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block; +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #212529; + text-align: left; + background-color: #fff; +} + +[tabindex="-1"]:focus { + outline: 0 !important; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0.5rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +abbr[title], +abbr[data-original-title] { + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none; +} + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; +} + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; +} + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; +} + +dt { + font-weight: 700; +} + +dd { + margin-bottom: .5rem; + margin-left: 0; +} + +blockquote { + margin: 0 0 1rem; +} + +b, +strong { + font-weight: bolder; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sub { + bottom: -.25em; +} + +sup { + top: -.5em; +} + +a { + color: #007bff; + text-decoration: none; + background-color: transparent; +} + +a:hover { + color: #0056b3; + text-decoration: underline; +} + +a:not([href]):not([tabindex]) { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):focus { + outline: 0; +} + +pre, +code, +kbd, +samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 1em; +} + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; +} + +figure { + margin: 0 0 1rem; +} + +img { + vertical-align: middle; + border-style: none; +} + +svg { + overflow: hidden; + vertical-align: middle; +} + +table { + border-collapse: collapse; +} + +caption { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #6c757d; + text-align: left; + caption-side: bottom; +} + +th { + text-align: inherit; +} + +label { + display: inline-block; + margin-bottom: 0.5rem; +} + +button { + border-radius: 0; +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +select { + word-wrap: normal; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +button:not(:disabled), +[type="button"]:not(:disabled), +[type="reset"]:not(:disabled), +[type="submit"]:not(:disabled) { + cursor: pointer; +} + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + padding: 0; + border-style: none; +} + +input[type="radio"], +input[type="checkbox"] { + box-sizing: border-box; + padding: 0; +} + +input[type="date"], +input[type="time"], +input[type="datetime-local"], +input[type="month"] { + -webkit-appearance: listbox; +} + +textarea { + overflow: auto; + resize: vertical; +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: .5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + outline-offset: -2px; + -webkit-appearance: none; +} + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; +} + +output { + display: inline-block; +} + +summary { + display: list-item; + cursor: pointer; +} + +template { + display: none; +} + +[hidden] { + display: none !important; +} + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; +} + +h1, .h1 { + font-size: 2.5rem; +} + +h2, .h2 { + font-size: 2rem; +} + +h3, .h3 { + font-size: 1.75rem; +} + +h4, .h4 { + font-size: 1.5rem; +} + +h5, .h5 { + font-size: 1.25rem; +} + +h6, .h6 { + font-size: 1rem; +} + +.lead { + font-size: 1.25rem; + font-weight: 300; +} + +.display-1 { + font-size: 6rem; + font-weight: 300; + line-height: 1.2; +} + +.display-2 { + font-size: 5.5rem; + font-weight: 300; + line-height: 1.2; +} + +.display-3 { + font-size: 4.5rem; + font-weight: 300; + line-height: 1.2; +} + +.display-4 { + font-size: 3.5rem; + font-weight: 300; + line-height: 1.2; +} + +hr { + margin-top: 1rem; + margin-bottom: 1rem; + border: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +small, +.small { + font-size: 80%; + font-weight: 400; +} + +mark, +.mark { + padding: 0.2em; + background-color: #fcf8e3; +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + list-style: none; +} + +.list-inline-item { + display: inline-block; +} + +.list-inline-item:not(:last-child) { + margin-right: 0.5rem; +} + +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem; +} + +.blockquote-footer { + display: block; + font-size: 80%; + color: #6c757d; +} + +.blockquote-footer::before { + content: "\2014\00A0"; +} + +.img-fluid { + max-width: 100%; + height: auto; +} + +.img-thumbnail { + padding: 0.25rem; + background-color: #fff; + border: 1px solid #dee2e6; + border-radius: 0.25rem; + max-width: 100%; + height: auto; +} + +.figure { + display: inline-block; +} + +.figure-img { + margin-bottom: 0.5rem; + line-height: 1; +} + +.figure-caption { + font-size: 90%; + color: #6c757d; +} + +code { + font-size: 87.5%; + color: #e83e8c; + word-break: break-word; +} + +a > code { + color: inherit; +} + +kbd { + padding: 0.2rem 0.4rem; + font-size: 87.5%; + color: #fff; + background-color: #212529; + border-radius: 0.2rem; +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; +} + +pre { + display: block; + font-size: 87.5%; + color: #212529; +} + +pre code { + font-size: inherit; + color: inherit; + word-break: normal; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1140px; + } +} + +.container-fluid { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +.row { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px; +} + +.no-gutters { + margin-right: 0; + margin-left: 0; +} + +.no-gutters > .col, +.no-gutters > [class*="col-"] { + padding-right: 0; + padding-left: 0; +} + +.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, +.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, +.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, +.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, +.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, +.col-xl-auto { + position: relative; + width: 100%; + padding-right: 15px; + padding-left: 15px; +} + +.col { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; +} + +.col-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; +} + +.col-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; +} + +.col-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; +} + +.col-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; +} + +.col-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; +} + +.col-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; +} + +.col-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; +} + +.col-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; +} + +.col-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; +} + +.col-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; +} + +.col-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; +} + +.col-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; +} + +.col-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; +} + +.order-first { + -ms-flex-order: -1; + order: -1; +} + +.order-last { + -ms-flex-order: 13; + order: 13; +} + +.order-0 { + -ms-flex-order: 0; + order: 0; +} + +.order-1 { + -ms-flex-order: 1; + order: 1; +} + +.order-2 { + -ms-flex-order: 2; + order: 2; +} + +.order-3 { + -ms-flex-order: 3; + order: 3; +} + +.order-4 { + -ms-flex-order: 4; + order: 4; +} + +.order-5 { + -ms-flex-order: 5; + order: 5; +} + +.order-6 { + -ms-flex-order: 6; + order: 6; +} + +.order-7 { + -ms-flex-order: 7; + order: 7; +} + +.order-8 { + -ms-flex-order: 8; + order: 8; +} + +.order-9 { + -ms-flex-order: 9; + order: 9; +} + +.order-10 { + -ms-flex-order: 10; + order: 10; +} + +.order-11 { + -ms-flex-order: 11; + order: 11; +} + +.order-12 { + -ms-flex-order: 12; + order: 12; +} + +.offset-1 { + margin-left: 8.333333%; +} + +.offset-2 { + margin-left: 16.666667%; +} + +.offset-3 { + margin-left: 25%; +} + +.offset-4 { + margin-left: 33.333333%; +} + +.offset-5 { + margin-left: 41.666667%; +} + +.offset-6 { + margin-left: 50%; +} + +.offset-7 { + margin-left: 58.333333%; +} + +.offset-8 { + margin-left: 66.666667%; +} + +.offset-9 { + margin-left: 75%; +} + +.offset-10 { + margin-left: 83.333333%; +} + +.offset-11 { + margin-left: 91.666667%; +} + +@media (min-width: 576px) { + .col-sm { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + .col-sm-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-sm-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-sm-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-sm-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-sm-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-sm-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-sm-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-sm-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-sm-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-sm-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-sm-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-sm-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-sm-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-sm-first { + -ms-flex-order: -1; + order: -1; + } + .order-sm-last { + -ms-flex-order: 13; + order: 13; + } + .order-sm-0 { + -ms-flex-order: 0; + order: 0; + } + .order-sm-1 { + -ms-flex-order: 1; + order: 1; + } + .order-sm-2 { + -ms-flex-order: 2; + order: 2; + } + .order-sm-3 { + -ms-flex-order: 3; + order: 3; + } + .order-sm-4 { + -ms-flex-order: 4; + order: 4; + } + .order-sm-5 { + -ms-flex-order: 5; + order: 5; + } + .order-sm-6 { + -ms-flex-order: 6; + order: 6; + } + .order-sm-7 { + -ms-flex-order: 7; + order: 7; + } + .order-sm-8 { + -ms-flex-order: 8; + order: 8; + } + .order-sm-9 { + -ms-flex-order: 9; + order: 9; + } + .order-sm-10 { + -ms-flex-order: 10; + order: 10; + } + .order-sm-11 { + -ms-flex-order: 11; + order: 11; + } + .order-sm-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-sm-0 { + margin-left: 0; + } + .offset-sm-1 { + margin-left: 8.333333%; + } + .offset-sm-2 { + margin-left: 16.666667%; + } + .offset-sm-3 { + margin-left: 25%; + } + .offset-sm-4 { + margin-left: 33.333333%; + } + .offset-sm-5 { + margin-left: 41.666667%; + } + .offset-sm-6 { + margin-left: 50%; + } + .offset-sm-7 { + margin-left: 58.333333%; + } + .offset-sm-8 { + margin-left: 66.666667%; + } + .offset-sm-9 { + margin-left: 75%; + } + .offset-sm-10 { + margin-left: 83.333333%; + } + .offset-sm-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 768px) { + .col-md { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + .col-md-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-md-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-md-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-md-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-md-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-md-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-md-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-md-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-md-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-md-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-md-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-md-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-md-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-md-first { + -ms-flex-order: -1; + order: -1; + } + .order-md-last { + -ms-flex-order: 13; + order: 13; + } + .order-md-0 { + -ms-flex-order: 0; + order: 0; + } + .order-md-1 { + -ms-flex-order: 1; + order: 1; + } + .order-md-2 { + -ms-flex-order: 2; + order: 2; + } + .order-md-3 { + -ms-flex-order: 3; + order: 3; + } + .order-md-4 { + -ms-flex-order: 4; + order: 4; + } + .order-md-5 { + -ms-flex-order: 5; + order: 5; + } + .order-md-6 { + -ms-flex-order: 6; + order: 6; + } + .order-md-7 { + -ms-flex-order: 7; + order: 7; + } + .order-md-8 { + -ms-flex-order: 8; + order: 8; + } + .order-md-9 { + -ms-flex-order: 9; + order: 9; + } + .order-md-10 { + -ms-flex-order: 10; + order: 10; + } + .order-md-11 { + -ms-flex-order: 11; + order: 11; + } + .order-md-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-md-0 { + margin-left: 0; + } + .offset-md-1 { + margin-left: 8.333333%; + } + .offset-md-2 { + margin-left: 16.666667%; + } + .offset-md-3 { + margin-left: 25%; + } + .offset-md-4 { + margin-left: 33.333333%; + } + .offset-md-5 { + margin-left: 41.666667%; + } + .offset-md-6 { + margin-left: 50%; + } + .offset-md-7 { + margin-left: 58.333333%; + } + .offset-md-8 { + margin-left: 66.666667%; + } + .offset-md-9 { + margin-left: 75%; + } + .offset-md-10 { + margin-left: 83.333333%; + } + .offset-md-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 992px) { + .col-lg { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + .col-lg-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-lg-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-lg-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-lg-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-lg-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-lg-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-lg-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-lg-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-lg-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-lg-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-lg-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-lg-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-lg-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-lg-first { + -ms-flex-order: -1; + order: -1; + } + .order-lg-last { + -ms-flex-order: 13; + order: 13; + } + .order-lg-0 { + -ms-flex-order: 0; + order: 0; + } + .order-lg-1 { + -ms-flex-order: 1; + order: 1; + } + .order-lg-2 { + -ms-flex-order: 2; + order: 2; + } + .order-lg-3 { + -ms-flex-order: 3; + order: 3; + } + .order-lg-4 { + -ms-flex-order: 4; + order: 4; + } + .order-lg-5 { + -ms-flex-order: 5; + order: 5; + } + .order-lg-6 { + -ms-flex-order: 6; + order: 6; + } + .order-lg-7 { + -ms-flex-order: 7; + order: 7; + } + .order-lg-8 { + -ms-flex-order: 8; + order: 8; + } + .order-lg-9 { + -ms-flex-order: 9; + order: 9; + } + .order-lg-10 { + -ms-flex-order: 10; + order: 10; + } + .order-lg-11 { + -ms-flex-order: 11; + order: 11; + } + .order-lg-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-lg-0 { + margin-left: 0; + } + .offset-lg-1 { + margin-left: 8.333333%; + } + .offset-lg-2 { + margin-left: 16.666667%; + } + .offset-lg-3 { + margin-left: 25%; + } + .offset-lg-4 { + margin-left: 33.333333%; + } + .offset-lg-5 { + margin-left: 41.666667%; + } + .offset-lg-6 { + margin-left: 50%; + } + .offset-lg-7 { + margin-left: 58.333333%; + } + .offset-lg-8 { + margin-left: 66.666667%; + } + .offset-lg-9 { + margin-left: 75%; + } + .offset-lg-10 { + margin-left: 83.333333%; + } + .offset-lg-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 1200px) { + .col-xl { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + .col-xl-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-xl-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-xl-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-xl-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-xl-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-xl-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-xl-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-xl-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-xl-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-xl-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-xl-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-xl-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-xl-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-xl-first { + -ms-flex-order: -1; + order: -1; + } + .order-xl-last { + -ms-flex-order: 13; + order: 13; + } + .order-xl-0 { + -ms-flex-order: 0; + order: 0; + } + .order-xl-1 { + -ms-flex-order: 1; + order: 1; + } + .order-xl-2 { + -ms-flex-order: 2; + order: 2; + } + .order-xl-3 { + -ms-flex-order: 3; + order: 3; + } + .order-xl-4 { + -ms-flex-order: 4; + order: 4; + } + .order-xl-5 { + -ms-flex-order: 5; + order: 5; + } + .order-xl-6 { + -ms-flex-order: 6; + order: 6; + } + .order-xl-7 { + -ms-flex-order: 7; + order: 7; + } + .order-xl-8 { + -ms-flex-order: 8; + order: 8; + } + .order-xl-9 { + -ms-flex-order: 9; + order: 9; + } + .order-xl-10 { + -ms-flex-order: 10; + order: 10; + } + .order-xl-11 { + -ms-flex-order: 11; + order: 11; + } + .order-xl-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-xl-0 { + margin-left: 0; + } + .offset-xl-1 { + margin-left: 8.333333%; + } + .offset-xl-2 { + margin-left: 16.666667%; + } + .offset-xl-3 { + margin-left: 25%; + } + .offset-xl-4 { + margin-left: 33.333333%; + } + .offset-xl-5 { + margin-left: 41.666667%; + } + .offset-xl-6 { + margin-left: 50%; + } + .offset-xl-7 { + margin-left: 58.333333%; + } + .offset-xl-8 { + margin-left: 66.666667%; + } + .offset-xl-9 { + margin-left: 75%; + } + .offset-xl-10 { + margin-left: 83.333333%; + } + .offset-xl-11 { + margin-left: 91.666667%; + } +} + +.table { + width: 100%; + margin-bottom: 1rem; + color: #212529; +} + +.table th, +.table td { + padding: 0.75rem; + vertical-align: top; + border-top: 1px solid #dee2e6; +} + +.table thead th { + vertical-align: bottom; + border-bottom: 2px solid #dee2e6; +} + +.table tbody + tbody { + border-top: 2px solid #dee2e6; +} + +.table-sm th, +.table-sm td { + padding: 0.3rem; +} + +.table-bordered { + border: 1px solid #dee2e6; +} + +.table-bordered th, +.table-bordered td { + border: 1px solid #dee2e6; +} + +.table-bordered thead th, +.table-bordered thead td { + border-bottom-width: 2px; +} + +.table-borderless th, +.table-borderless td, +.table-borderless thead th, +.table-borderless tbody + tbody { + border: 0; +} + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, 0.05); +} + +.table-hover tbody tr:hover { + color: #212529; + background-color: rgba(0, 0, 0, 0.075); +} + +.table-primary, +.table-primary > th, +.table-primary > td { + background-color: #b8daff; +} + +.table-primary th, +.table-primary td, +.table-primary thead th, +.table-primary tbody + tbody { + border-color: #7abaff; +} + +.table-hover .table-primary:hover { + background-color: #9fcdff; +} + +.table-hover .table-primary:hover > td, +.table-hover .table-primary:hover > th { + background-color: #9fcdff; +} + +.table-secondary, +.table-secondary > th, +.table-secondary > td { + background-color: #d6d8db; +} + +.table-secondary th, +.table-secondary td, +.table-secondary thead th, +.table-secondary tbody + tbody { + border-color: #b3b7bb; +} + +.table-hover .table-secondary:hover { + background-color: #c8cbcf; +} + +.table-hover .table-secondary:hover > td, +.table-hover .table-secondary:hover > th { + background-color: #c8cbcf; +} + +.table-success, +.table-success > th, +.table-success > td { + background-color: #c3e6cb; +} + +.table-success th, +.table-success td, +.table-success thead th, +.table-success tbody + tbody { + border-color: #8fd19e; +} + +.table-hover .table-success:hover { + background-color: #b1dfbb; +} + +.table-hover .table-success:hover > td, +.table-hover .table-success:hover > th { + background-color: #b1dfbb; +} + +.table-info, +.table-info > th, +.table-info > td { + background-color: #bee5eb; +} + +.table-info th, +.table-info td, +.table-info thead th, +.table-info tbody + tbody { + border-color: #86cfda; +} + +.table-hover .table-info:hover { + background-color: #abdde5; +} + +.table-hover .table-info:hover > td, +.table-hover .table-info:hover > th { + background-color: #abdde5; +} + +.table-warning, +.table-warning > th, +.table-warning > td { + background-color: #ffeeba; +} + +.table-warning th, +.table-warning td, +.table-warning thead th, +.table-warning tbody + tbody { + border-color: #ffdf7e; +} + +.table-hover .table-warning:hover { + background-color: #ffe8a1; +} + +.table-hover .table-warning:hover > td, +.table-hover .table-warning:hover > th { + background-color: #ffe8a1; +} + +.table-danger, +.table-danger > th, +.table-danger > td { + background-color: #f5c6cb; +} + +.table-danger th, +.table-danger td, +.table-danger thead th, +.table-danger tbody + tbody { + border-color: #ed969e; +} + +.table-hover .table-danger:hover { + background-color: #f1b0b7; +} + +.table-hover .table-danger:hover > td, +.table-hover .table-danger:hover > th { + background-color: #f1b0b7; +} + +.table-light, +.table-light > th, +.table-light > td { + background-color: #fdfdfe; +} + +.table-light th, +.table-light td, +.table-light thead th, +.table-light tbody + tbody { + border-color: #fbfcfc; +} + +.table-hover .table-light:hover { + background-color: #ececf6; +} + +.table-hover .table-light:hover > td, +.table-hover .table-light:hover > th { + background-color: #ececf6; +} + +.table-dark, +.table-dark > th, +.table-dark > td { + background-color: #c6c8ca; +} + +.table-dark th, +.table-dark td, +.table-dark thead th, +.table-dark tbody + tbody { + border-color: #95999c; +} + +.table-hover .table-dark:hover { + background-color: #b9bbbe; +} + +.table-hover .table-dark:hover > td, +.table-hover .table-dark:hover > th { + background-color: #b9bbbe; +} + +.table-active, +.table-active > th, +.table-active > td { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-hover .table-active:hover { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-hover .table-active:hover > td, +.table-hover .table-active:hover > th { + background-color: rgba(0, 0, 0, 0.075); +} + +.table .thead-dark th { + color: #fff; + background-color: #343a40; + border-color: #454d55; +} + +.table .thead-light th { + color: #495057; + background-color: #e9ecef; + border-color: #dee2e6; +} + +.table-dark { + color: #fff; + background-color: #343a40; +} + +.table-dark th, +.table-dark td, +.table-dark thead th { + border-color: #454d55; +} + +.table-dark.table-bordered { + border: 0; +} + +.table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.05); +} + +.table-dark.table-hover tbody tr:hover { + color: #fff; + background-color: rgba(255, 255, 255, 0.075); +} + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .table-responsive-sm > .table-bordered { + border: 0; + } +} + +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .table-responsive-md > .table-bordered { + border: 0; + } +} + +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .table-responsive-lg > .table-bordered { + border: 0; + } +} + +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .table-responsive-xl > .table-bordered { + border: 0; + } +} + +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + +.table-responsive > .table-bordered { + border: 0; +} + +.form-control { + display: block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .form-control { + transition: none; + } +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} + +.form-control:focus { + color: #495057; + background-color: #fff; + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.form-control::-webkit-input-placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control::-moz-placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control:-ms-input-placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control::-ms-input-placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control::placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control:disabled, .form-control[readonly] { + background-color: #e9ecef; + opacity: 1; +} + +select.form-control:focus::-ms-value { + color: #495057; + background-color: #fff; +} + +.form-control-file, +.form-control-range { + display: block; + width: 100%; +} + +.col-form-label { + padding-top: calc(0.375rem + 1px); + padding-bottom: calc(0.375rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; +} + +.col-form-label-lg { + padding-top: calc(0.5rem + 1px); + padding-bottom: calc(0.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5; +} + +.col-form-label-sm { + padding-top: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px); + font-size: 0.875rem; + line-height: 1.5; +} + +.form-control-plaintext { + display: block; + width: 100%; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + margin-bottom: 0; + line-height: 1.5; + color: #212529; + background-color: transparent; + border: solid transparent; + border-width: 1px 0; +} + +.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { + padding-right: 0; + padding-left: 0; +} + +.form-control-sm { + height: calc(1.5em + 0.5rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.form-control-lg { + height: calc(1.5em + 1rem + 2px); + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +select.form-control[size], select.form-control[multiple] { + height: auto; +} + +textarea.form-control { + height: auto; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-text { + display: block; + margin-top: 0.25rem; +} + +.form-row { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; +} + +.form-row > .col, +.form-row > [class*="col-"] { + padding-right: 5px; + padding-left: 5px; +} + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem; +} + +.form-check-input { + position: absolute; + margin-top: 0.3rem; + margin-left: -1.25rem; +} + +.form-check-input:disabled ~ .form-check-label { + color: #6c757d; +} + +.form-check-label { + margin-bottom: 0; +} + +.form-check-inline { + display: -ms-inline-flexbox; + display: inline-flex; + -ms-flex-align: center; + align-items: center; + padding-left: 0; + margin-right: 0.75rem; +} + +.form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: 0.3125rem; + margin-left: 0; +} + +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #28a745; +} + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(40, 167, 69, 0.9); + border-radius: 0.25rem; +} + +.was-validated .form-control:valid, .form-control.is-valid { + border-color: #28a745; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: center right calc(0.375em + 0.1875rem); + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .form-control:valid:focus, .form-control.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .form-control:valid ~ .valid-feedback, +.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, +.form-control.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); +} + +.was-validated .custom-select:valid, .custom-select.is-valid { + border-color: #28a745; + padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .custom-select:valid ~ .valid-feedback, +.was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback, +.custom-select.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .form-control-file:valid ~ .valid-feedback, +.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, +.form-control-file.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { + color: #28a745; +} + +.was-validated .form-check-input:valid ~ .valid-feedback, +.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, +.form-check-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { + color: #28a745; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { + border-color: #28a745; +} + +.was-validated .custom-control-input:valid ~ .valid-feedback, +.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, +.custom-control-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { + border-color: #34ce57; + background-color: #34ce57; +} + +.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #28a745; +} + +.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { + border-color: #28a745; +} + +.was-validated .custom-file-input:valid ~ .valid-feedback, +.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, +.custom-file-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; +} + +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(220, 53, 69, 0.9); + border-radius: 0.25rem; +} + +.was-validated .form-control:invalid, .form-control.is-invalid { + border-color: #dc3545; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); + background-repeat: no-repeat; + background-position: center right calc(0.375em + 0.1875rem); + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .form-control:invalid ~ .invalid-feedback, +.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, +.form-control.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); +} + +.was-validated .custom-select:invalid, .custom-select.is-invalid { + border-color: #dc3545; + padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .custom-select:invalid ~ .invalid-feedback, +.was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback, +.custom-select.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .form-control-file:invalid ~ .invalid-feedback, +.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, +.form-control-file.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { + color: #dc3545; +} + +.was-validated .form-check-input:invalid ~ .invalid-feedback, +.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, +.form-check-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { + color: #dc3545; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { + border-color: #dc3545; +} + +.was-validated .custom-control-input:invalid ~ .invalid-feedback, +.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, +.custom-control-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { + border-color: #e4606d; + background-color: #e4606d; +} + +.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #dc3545; +} + +.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { + border-color: #dc3545; +} + +.was-validated .custom-file-input:invalid ~ .invalid-feedback, +.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, +.custom-file-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.form-inline { + display: -ms-flexbox; + display: flex; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -ms-flex-align: center; + align-items: center; +} + +.form-inline .form-check { + width: 100%; +} + +@media (min-width: 576px) { + .form-inline label { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + margin-bottom: 0; + } + .form-inline .form-group { + display: -ms-flexbox; + display: flex; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -ms-flex-align: center; + align-items: center; + margin-bottom: 0; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-plaintext { + display: inline-block; + } + .form-inline .input-group, + .form-inline .custom-select { + width: auto; + } + .form-inline .form-check { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + width: auto; + padding-left: 0; + } + .form-inline .form-check-input { + position: relative; + -ms-flex-negative: 0; + flex-shrink: 0; + margin-top: 0; + margin-right: 0.25rem; + margin-left: 0; + } + .form-inline .custom-control { + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + } + .form-inline .custom-control-label { + margin-bottom: 0; + } +} + +.btn { + display: inline-block; + font-weight: 400; + color: #212529; + text-align: center; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .btn { + transition: none; + } +} + +.btn:hover { + color: #212529; + text-decoration: none; +} + +.btn:focus, .btn.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.btn.disabled, .btn:disabled { + opacity: 0.65; +} + +a.btn.disabled, +fieldset:disabled a.btn { + pointer-events: none; +} + +.btn-primary { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-primary:hover { + color: #fff; + background-color: #0069d9; + border-color: #0062cc; +} + +.btn-primary:focus, .btn-primary.focus { + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, +.show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #0062cc; + border-color: #005cbf; +} + +.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, +.show > .btn-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.btn-secondary { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-secondary:hover { + color: #fff; + background-color: #5a6268; + border-color: #545b62; +} + +.btn-secondary:focus, .btn-secondary.focus { + box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); +} + +.btn-secondary.disabled, .btn-secondary:disabled { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, +.show > .btn-secondary.dropdown-toggle { + color: #fff; + background-color: #545b62; + border-color: #4e555b; +} + +.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, +.show > .btn-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); +} + +.btn-success { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-success:hover { + color: #fff; + background-color: #218838; + border-color: #1e7e34; +} + +.btn-success:focus, .btn-success.focus { + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); +} + +.btn-success.disabled, .btn-success:disabled { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, +.show > .btn-success.dropdown-toggle { + color: #fff; + background-color: #1e7e34; + border-color: #1c7430; +} + +.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, +.show > .btn-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); +} + +.btn-info { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-info:hover { + color: #fff; + background-color: #138496; + border-color: #117a8b; +} + +.btn-info:focus, .btn-info.focus { + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); +} + +.btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, +.show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #117a8b; + border-color: #10707f; +} + +.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, +.show > .btn-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); +} + +.btn-warning { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-warning:hover { + color: #212529; + background-color: #e0a800; + border-color: #d39e00; +} + +.btn-warning:focus, .btn-warning.focus { + box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); +} + +.btn-warning.disabled, .btn-warning:disabled { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, +.show > .btn-warning.dropdown-toggle { + color: #212529; + background-color: #d39e00; + border-color: #c69500; +} + +.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, +.show > .btn-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); +} + +.btn-danger { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-danger:hover { + color: #fff; + background-color: #c82333; + border-color: #bd2130; +} + +.btn-danger:focus, .btn-danger.focus { + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); +} + +.btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, +.show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #bd2130; + border-color: #b21f2d; +} + +.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, +.show > .btn-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); +} + +.btn-light { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-light:hover { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; +} + +.btn-light:focus, .btn-light.focus { + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); +} + +.btn-light.disabled, .btn-light:disabled { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, +.show > .btn-light.dropdown-toggle { + color: #212529; + background-color: #dae0e5; + border-color: #d3d9df; +} + +.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, +.show > .btn-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); +} + +.btn-dark { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-dark:hover { + color: #fff; + background-color: #23272b; + border-color: #1d2124; +} + +.btn-dark:focus, .btn-dark.focus { + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); +} + +.btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, +.show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #1d2124; + border-color: #171a1d; +} + +.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, +.show > .btn-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); +} + +.btn-outline-primary { + color: #007bff; + border-color: #007bff; +} + +.btn-outline-primary:hover { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-outline-primary:focus, .btn-outline-primary.focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #007bff; + background-color: transparent; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, +.show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.btn-outline-secondary { + color: #6c757d; + border-color: #6c757d; +} + +.btn-outline-secondary:hover { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-outline-secondary:focus, .btn-outline-secondary.focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #6c757d; + background-color: transparent; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, +.show > .btn-outline-secondary.dropdown-toggle { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.btn-outline-success { + color: #28a745; + border-color: #28a745; +} + +.btn-outline-success:hover { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-outline-success:focus, .btn-outline-success.focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-outline-success.disabled, .btn-outline-success:disabled { + color: #28a745; + background-color: transparent; +} + +.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, +.show > .btn-outline-success.dropdown-toggle { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-outline-info { + color: #17a2b8; + border-color: #17a2b8; +} + +.btn-outline-info:hover { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-outline-info:focus, .btn-outline-info.focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-outline-info.disabled, .btn-outline-info:disabled { + color: #17a2b8; + background-color: transparent; +} + +.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, +.show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-outline-warning { + color: #ffc107; + border-color: #ffc107; +} + +.btn-outline-warning:hover { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-outline-warning:focus, .btn-outline-warning.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #ffc107; + background-color: transparent; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, +.show > .btn-outline-warning.dropdown-toggle { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.btn-outline-danger { + color: #dc3545; + border-color: #dc3545; +} + +.btn-outline-danger:hover { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-outline-danger:focus, .btn-outline-danger.focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #dc3545; + background-color: transparent; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, +.show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-outline-light { + color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-outline-light:hover { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-outline-light:focus, .btn-outline-light.focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-outline-light.disabled, .btn-outline-light:disabled { + color: #f8f9fa; + background-color: transparent; +} + +.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, +.show > .btn-outline-light.dropdown-toggle { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-outline-dark { + color: #343a40; + border-color: #343a40; +} + +.btn-outline-dark:hover { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-outline-dark:focus, .btn-outline-dark.focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #343a40; + background-color: transparent; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, +.show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-link { + font-weight: 400; + color: #007bff; + text-decoration: none; +} + +.btn-link:hover { + color: #0056b3; + text-decoration: underline; +} + +.btn-link:focus, .btn-link.focus { + text-decoration: underline; + box-shadow: none; +} + +.btn-link:disabled, .btn-link.disabled { + color: #6c757d; + pointer-events: none; +} + +.btn-lg, .btn-group-lg > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +.btn-sm, .btn-group-sm > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.btn-block { + display: block; + width: 100%; +} + +.btn-block + .btn-block { + margin-top: 0.5rem; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.fade { + transition: opacity 0.15s linear; +} + +@media (prefers-reduced-motion: reduce) { + .fade { + transition: none; + } +} + +.fade:not(.show) { + opacity: 0; +} + +.collapse:not(.show) { + display: none; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height 0.35s ease; +} + +@media (prefers-reduced-motion: reduce) { + .collapsing { + transition: none; + } +} + +.dropup, +.dropright, +.dropdown, +.dropleft { + position: relative; +} + +.dropdown-toggle { + white-space: nowrap; +} + +.dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; +} + +.dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: 0.5rem 0; + margin: 0.125rem 0 0; + font-size: 1rem; + color: #212529; + text-align: left; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25rem; +} + +.dropdown-menu-left { + right: auto; + left: 0; +} + +.dropdown-menu-right { + right: 0; + left: auto; +} + +@media (min-width: 576px) { + .dropdown-menu-sm-left { + right: auto; + left: 0; + } + .dropdown-menu-sm-right { + right: 0; + left: auto; + } +} + +@media (min-width: 768px) { + .dropdown-menu-md-left { + right: auto; + left: 0; + } + .dropdown-menu-md-right { + right: 0; + left: auto; + } +} + +@media (min-width: 992px) { + .dropdown-menu-lg-left { + right: auto; + left: 0; + } + .dropdown-menu-lg-right { + right: 0; + left: auto; + } +} + +@media (min-width: 1200px) { + .dropdown-menu-xl-left { + right: auto; + left: 0; + } + .dropdown-menu-xl-right { + right: 0; + left: auto; + } +} + +.dropup .dropdown-menu { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: 0.125rem; +} + +.dropup .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; +} + +.dropup .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-menu { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: 0.125rem; +} + +.dropright .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; +} + +.dropright .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-toggle::after { + vertical-align: 0; +} + +.dropleft .dropdown-menu { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: 0.125rem; +} + +.dropleft .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; +} + +.dropleft .dropdown-toggle::after { + display: none; +} + +.dropleft .dropdown-toggle::before { + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; +} + +.dropleft .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle::before { + vertical-align: 0; +} + +.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { + right: auto; + bottom: auto; +} + +.dropdown-divider { + height: 0; + margin: 0.5rem 0; + overflow: hidden; + border-top: 1px solid #e9ecef; +} + +.dropdown-item { + display: block; + width: 100%; + padding: 0.25rem 1.5rem; + clear: both; + font-weight: 400; + color: #212529; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0; +} + +.dropdown-item:hover, .dropdown-item:focus { + color: #16181b; + text-decoration: none; + background-color: #f8f9fa; +} + +.dropdown-item.active, .dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: #007bff; +} + +.dropdown-item.disabled, .dropdown-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: transparent; +} + +.dropdown-menu.show { + display: block; +} + +.dropdown-header { + display: block; + padding: 0.5rem 1.5rem; + margin-bottom: 0; + font-size: 0.875rem; + color: #6c757d; + white-space: nowrap; +} + +.dropdown-item-text { + display: block; + padding: 0.25rem 1.5rem; + color: #212529; +} + +.btn-group, +.btn-group-vertical { + position: relative; + display: -ms-inline-flexbox; + display: inline-flex; + vertical-align: middle; +} + +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + -ms-flex: 1 1 auto; + flex: 1 1 auto; +} + +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover { + z-index: 1; +} + +.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, +.btn-group-vertical > .btn:focus, +.btn-group-vertical > .btn:active, +.btn-group-vertical > .btn.active { + z-index: 1; +} + +.btn-toolbar { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.btn-toolbar .input-group { + width: auto; +} + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) { + margin-left: -1px; +} + +.btn-group > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.dropdown-toggle-split { + padding-right: 0.5625rem; + padding-left: 0.5625rem; +} + +.dropdown-toggle-split::after, +.dropup .dropdown-toggle-split::after, +.dropright .dropdown-toggle-split::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle-split::before { + margin-right: 0; +} + +.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { + padding-right: 0.375rem; + padding-left: 0.375rem; +} + +.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.btn-group-vertical { + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-align: start; + align-items: flex-start; + -ms-flex-pack: center; + justify-content: center; +} + +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group { + width: 100%; +} + +.btn-group-vertical > .btn:not(:first-child), +.btn-group-vertical > .btn-group:not(:first-child) { + margin-top: -1px; +} + +.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group-vertical > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn:not(:first-child), +.btn-group-vertical > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.btn-group-toggle > .btn, +.btn-group-toggle > .btn-group > .btn { + margin-bottom: 0; +} + +.btn-group-toggle > .btn input[type="radio"], +.btn-group-toggle > .btn input[type="checkbox"], +.btn-group-toggle > .btn-group > .btn input[type="radio"], +.btn-group-toggle > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} + +.input-group { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: stretch; + align-items: stretch; + width: 100%; +} + +.input-group > .form-control, +.input-group > .form-control-plaintext, +.input-group > .custom-select, +.input-group > .custom-file { + position: relative; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + width: 1%; + margin-bottom: 0; +} + +.input-group > .form-control + .form-control, +.input-group > .form-control + .custom-select, +.input-group > .form-control + .custom-file, +.input-group > .form-control-plaintext + .form-control, +.input-group > .form-control-plaintext + .custom-select, +.input-group > .form-control-plaintext + .custom-file, +.input-group > .custom-select + .form-control, +.input-group > .custom-select + .custom-select, +.input-group > .custom-select + .custom-file, +.input-group > .custom-file + .form-control, +.input-group > .custom-file + .custom-select, +.input-group > .custom-file + .custom-file { + margin-left: -1px; +} + +.input-group > .form-control:focus, +.input-group > .custom-select:focus, +.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { + z-index: 3; +} + +.input-group > .custom-file .custom-file-input:focus { + z-index: 4; +} + +.input-group > .form-control:not(:last-child), +.input-group > .custom-select:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .form-control:not(:first-child), +.input-group > .custom-select:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group > .custom-file { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; +} + +.input-group > .custom-file:not(:last-child) .custom-file-label, +.input-group > .custom-file:not(:last-child) .custom-file-label::after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .custom-file:not(:first-child) .custom-file-label { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group-prepend, +.input-group-append { + display: -ms-flexbox; + display: flex; +} + +.input-group-prepend .btn, +.input-group-append .btn { + position: relative; + z-index: 2; +} + +.input-group-prepend .btn:focus, +.input-group-append .btn:focus { + z-index: 3; +} + +.input-group-prepend .btn + .btn, +.input-group-prepend .btn + .input-group-text, +.input-group-prepend .input-group-text + .input-group-text, +.input-group-prepend .input-group-text + .btn, +.input-group-append .btn + .btn, +.input-group-append .btn + .input-group-text, +.input-group-append .input-group-text + .input-group-text, +.input-group-append .input-group-text + .btn { + margin-left: -1px; +} + +.input-group-prepend { + margin-right: -1px; +} + +.input-group-append { + margin-left: -1px; +} + +.input-group-text { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + padding: 0.375rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: 0.25rem; +} + +.input-group-text input[type="radio"], +.input-group-text input[type="checkbox"] { + margin-top: 0; +} + +.input-group-lg > .form-control:not(textarea), +.input-group-lg > .custom-select { + height: calc(1.5em + 1rem + 2px); +} + +.input-group-lg > .form-control, +.input-group-lg > .custom-select, +.input-group-lg > .input-group-prepend > .input-group-text, +.input-group-lg > .input-group-append > .input-group-text, +.input-group-lg > .input-group-prepend > .btn, +.input-group-lg > .input-group-append > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +.input-group-sm > .form-control:not(textarea), +.input-group-sm > .custom-select { + height: calc(1.5em + 0.5rem + 2px); +} + +.input-group-sm > .form-control, +.input-group-sm > .custom-select, +.input-group-sm > .input-group-prepend > .input-group-text, +.input-group-sm > .input-group-append > .input-group-text, +.input-group-sm > .input-group-prepend > .btn, +.input-group-sm > .input-group-append > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.input-group-lg > .custom-select, +.input-group-sm > .custom-select { + padding-right: 1.75rem; +} + +.input-group > .input-group-prepend > .btn, +.input-group > .input-group-prepend > .input-group-text, +.input-group > .input-group-append:not(:last-child) > .btn, +.input-group > .input-group-append:not(:last-child) > .input-group-text, +.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .input-group-append > .btn, +.input-group > .input-group-append > .input-group-text, +.input-group > .input-group-prepend:not(:first-child) > .btn, +.input-group > .input-group-prepend:not(:first-child) > .input-group-text, +.input-group > .input-group-prepend:first-child > .btn:not(:first-child), +.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.custom-control { + position: relative; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem; +} + +.custom-control-inline { + display: -ms-inline-flexbox; + display: inline-flex; + margin-right: 1rem; +} + +.custom-control-input { + position: absolute; + z-index: -1; + opacity: 0; +} + +.custom-control-input:checked ~ .custom-control-label::before { + color: #fff; + border-color: #007bff; + background-color: #007bff; +} + +.custom-control-input:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { + border-color: #80bdff; +} + +.custom-control-input:not(:disabled):active ~ .custom-control-label::before { + color: #fff; + background-color: #b3d7ff; + border-color: #b3d7ff; +} + +.custom-control-input:disabled ~ .custom-control-label { + color: #6c757d; +} + +.custom-control-input:disabled ~ .custom-control-label::before { + background-color: #e9ecef; +} + +.custom-control-label { + position: relative; + margin-bottom: 0; + vertical-align: top; +} + +.custom-control-label::before { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + background-color: #fff; + border: #adb5bd solid 1px; +} + +.custom-control-label::after { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background: no-repeat 50% / 50% 50%; +} + +.custom-checkbox .custom-control-label::before { + border-radius: 0.25rem; +} + +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { + border-color: #007bff; + background-color: #007bff; +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); +} + +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-radio .custom-control-label::before { + border-radius: 50%; +} + +.custom-radio .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); +} + +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-switch { + padding-left: 2.25rem; +} + +.custom-switch .custom-control-label::before { + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: 0.5rem; +} + +.custom-switch .custom-control-label::after { + top: calc(0.25rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #adb5bd; + border-radius: 0.5rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .custom-switch .custom-control-label::after { + transition: none; + } +} + +.custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: #fff; + -webkit-transform: translateX(0.75rem); + transform: translateX(0.75rem); +} + +.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-select { + display: inline-block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 1.75rem 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + vertical-align: middle; + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: 0.25rem; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.custom-select:focus { + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-select:focus::-ms-value { + color: #495057; + background-color: #fff; +} + +.custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: 0.75rem; + background-image: none; +} + +.custom-select:disabled { + color: #6c757d; + background-color: #e9ecef; +} + +.custom-select::-ms-expand { + display: none; +} + +.custom-select-sm { + height: calc(1.5em + 0.5rem + 2px); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + font-size: 0.875rem; +} + +.custom-select-lg { + height: calc(1.5em + 1rem + 2px); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + font-size: 1.25rem; +} + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + margin-bottom: 0; +} + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + margin: 0; + opacity: 0; +} + +.custom-file-input:focus ~ .custom-file-label { + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-file-input:disabled ~ .custom-file-label { + background-color: #e9ecef; +} + +.custom-file-input:lang(en) ~ .custom-file-label::after { + content: "Browse"; +} + +.custom-file-input ~ .custom-file-label[data-browse]::after { + content: attr(data-browse); +} + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: 0.25rem; +} + +.custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: calc(1.5em + 0.75rem); + padding: 0.375rem 0.75rem; + line-height: 1.5; + color: #495057; + content: "Browse"; + background-color: #e9ecef; + border-left: inherit; + border-radius: 0 0.25rem 0.25rem 0; +} + +.custom-range { + width: 100%; + height: calc(1rem + 0.4rem); + padding: 0; + background-color: transparent; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.custom-range:focus { + outline: none; +} + +.custom-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-range::-moz-focus-outer { + border: 0; +} + +.custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + -webkit-appearance: none; + appearance: none; +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-webkit-slider-thumb { + transition: none; + } +} + +.custom-range::-webkit-slider-thumb:active { + background-color: #b3d7ff; +} + +.custom-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; +} + +.custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + -moz-appearance: none; + appearance: none; +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-moz-range-thumb { + transition: none; + } +} + +.custom-range::-moz-range-thumb:active { + background-color: #b3d7ff; +} + +.custom-range::-moz-range-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; +} + +.custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: 0.2rem; + margin-left: 0.2rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-ms-thumb { + transition: none; + } +} + +.custom-range::-ms-thumb:active { + background-color: #b3d7ff; +} + +.custom-range::-ms-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: 0.5rem; +} + +.custom-range::-ms-fill-lower { + background-color: #dee2e6; + border-radius: 1rem; +} + +.custom-range::-ms-fill-upper { + margin-right: 15px; + background-color: #dee2e6; + border-radius: 1rem; +} + +.custom-range:disabled::-webkit-slider-thumb { + background-color: #adb5bd; +} + +.custom-range:disabled::-webkit-slider-runnable-track { + cursor: default; +} + +.custom-range:disabled::-moz-range-thumb { + background-color: #adb5bd; +} + +.custom-range:disabled::-moz-range-track { + cursor: default; +} + +.custom-range:disabled::-ms-thumb { + background-color: #adb5bd; +} + +.custom-control-label::before, +.custom-file-label, +.custom-select { + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .custom-control-label::before, + .custom-file-label, + .custom-select { + transition: none; + } +} + +.nav { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav-link { + display: block; + padding: 0.5rem 1rem; +} + +.nav-link:hover, .nav-link:focus { + text-decoration: none; +} + +.nav-link.disabled { + color: #6c757d; + pointer-events: none; + cursor: default; +} + +.nav-tabs { + border-bottom: 1px solid #dee2e6; +} + +.nav-tabs .nav-item { + margin-bottom: -1px; +} + +.nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { + border-color: #e9ecef #e9ecef #dee2e6; +} + +.nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent; +} + +.nav-tabs .nav-link.active, +.nav-tabs .nav-item.show .nav-link { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff; +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.nav-pills .nav-link { + border-radius: 0.25rem; +} + +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: #fff; + background-color: #007bff; +} + +.nav-fill .nav-item { + -ms-flex: 1 1 auto; + flex: 1 1 auto; + text-align: center; +} + +.nav-justified .nav-item { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + text-align: center; +} + +.tab-content > .tab-pane { + display: none; +} + +.tab-content > .active { + display: block; +} + +.navbar { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 0.5rem 1rem; +} + +.navbar > .container, +.navbar > .container-fluid { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.navbar-brand { + display: inline-block; + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; +} + +.navbar-brand:hover, .navbar-brand:focus { + text-decoration: none; +} + +.navbar-nav { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; +} + +.navbar-nav .dropdown-menu { + position: static; + float: none; +} + +.navbar-text { + display: inline-block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.navbar-collapse { + -ms-flex-preferred-size: 100%; + flex-basis: 100%; + -ms-flex-positive: 1; + flex-grow: 1; + -ms-flex-align: center; + align-items: center; +} + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: 0.25rem; +} + +.navbar-toggler:hover, .navbar-toggler:focus { + text-decoration: none; +} + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: no-repeat center center; + background-size: 100% 100%; +} + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 576px) { + .navbar-expand-sm { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + } + .navbar-expand-sm .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + .navbar-expand-sm .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-expand-sm .navbar-toggler { + display: none; + } +} + +@media (max-width: 767.98px) { + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 768px) { + .navbar-expand-md { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + } + .navbar-expand-md .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + .navbar-expand-md .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-expand-md .navbar-toggler { + display: none; + } +} + +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 992px) { + .navbar-expand-lg { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + } + .navbar-expand-lg .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + .navbar-expand-lg .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-expand-lg .navbar-toggler { + display: none; + } +} + +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 1200px) { + .navbar-expand-xl { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + } + .navbar-expand-xl .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + .navbar-expand-xl .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-expand-xl .navbar-toggler { + display: none; + } +} + +.navbar-expand { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.navbar-expand > .container, +.navbar-expand > .container-fluid { + padding-right: 0; + padding-left: 0; +} + +.navbar-expand .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; +} + +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute; +} + +.navbar-expand .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; +} + +.navbar-expand > .container, +.navbar-expand > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; +} + +.navbar-expand .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; +} + +.navbar-expand .navbar-toggler { + display: none; +} + +.navbar-light .navbar-brand { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-nav .nav-link { + color: rgba(0, 0, 0, 0.5); +} + +.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { + color: rgba(0, 0, 0, 0.7); +} + +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, 0.3); +} + +.navbar-light .navbar-nav .show > .nav-link, +.navbar-light .navbar-nav .active > .nav-link, +.navbar-light .navbar-nav .nav-link.show, +.navbar-light .navbar-nav .nav-link.active { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-toggler { + color: rgba(0, 0, 0, 0.5); + border-color: rgba(0, 0, 0, 0.1); +} + +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} + +.navbar-light .navbar-text { + color: rgba(0, 0, 0, 0.5); +} + +.navbar-light .navbar-text a { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-dark .navbar-brand { + color: #fff; +} + +.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { + color: #fff; +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.5); +} + +.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { + color: rgba(255, 255, 255, 0.75); +} + +.navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); +} + +.navbar-dark .navbar-nav .show > .nav-link, +.navbar-dark .navbar-nav .active > .nav-link, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .nav-link.active { + color: #fff; +} + +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, 0.5); + border-color: rgba(255, 255, 255, 0.1); +} + +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} + +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, 0.5); +} + +.navbar-dark .navbar-text a { + color: #fff; +} + +.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { + color: #fff; +} + +.card { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #fff; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; +} + +.card > hr { + margin-right: 0; + margin-left: 0; +} + +.card > .list-group:first-child .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.card > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.card-body { + -ms-flex: 1 1 auto; + flex: 1 1 auto; + padding: 1.25rem; +} + +.card-title { + margin-bottom: 0.75rem; +} + +.card-subtitle { + margin-top: -0.375rem; + margin-bottom: 0; +} + +.card-text:last-child { + margin-bottom: 0; +} + +.card-link:hover { + text-decoration: none; +} + +.card-link + .card-link { + margin-left: 1.25rem; +} + +.card-header { + padding: 0.75rem 1.25rem; + margin-bottom: 0; + background-color: rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); +} + +.card-header:first-child { + border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; +} + +.card-header + .list-group .list-group-item:first-child { + border-top: 0; +} + +.card-footer { + padding: 0.75rem 1.25rem; + background-color: rgba(0, 0, 0, 0.03); + border-top: 1px solid rgba(0, 0, 0, 0.125); +} + +.card-footer:last-child { + border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); +} + +.card-header-tabs { + margin-right: -0.625rem; + margin-bottom: -0.75rem; + margin-left: -0.625rem; + border-bottom: 0; +} + +.card-header-pills { + margin-right: -0.625rem; + margin-left: -0.625rem; +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem; +} + +.card-img { + width: 100%; + border-radius: calc(0.25rem - 1px); +} + +.card-img-top { + width: 100%; + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); +} + +.card-img-bottom { + width: 100%; + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); +} + +.card-deck { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; +} + +.card-deck .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-deck { + -ms-flex-flow: row wrap; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; + } + .card-deck .card { + display: -ms-flexbox; + display: flex; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + -ms-flex-direction: column; + flex-direction: column; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; + } +} + +.card-group { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; +} + +.card-group > .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-group { + -ms-flex-flow: row wrap; + flex-flow: row wrap; + } + .card-group > .card { + -ms-flex: 1 0 0%; + flex: 1 0 0%; + margin-bottom: 0; + } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; + } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; + } + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { + border-bottom-right-radius: 0; + } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; + } + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; + } +} + +.card-columns .card { + margin-bottom: 0.75rem; +} + +@media (min-width: 576px) { + .card-columns { + -webkit-column-count: 3; + -moz-column-count: 3; + column-count: 3; + -webkit-column-gap: 1.25rem; + -moz-column-gap: 1.25rem; + column-gap: 1.25rem; + orphans: 1; + widows: 1; + } + .card-columns .card { + display: inline-block; + width: 100%; + } +} + +.accordion > .card { + overflow: hidden; +} + +.accordion > .card:not(:first-of-type) .card-header:first-child { + border-radius: 0; +} + +.accordion > .card:not(:first-of-type):not(:last-of-type) { + border-bottom: 0; + border-radius: 0; +} + +.accordion > .card:first-of-type { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.accordion > .card:last-of-type { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.accordion > .card .card-header { + margin-bottom: -1px; +} + +.breadcrumb { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #e9ecef; + border-radius: 0.25rem; +} + +.breadcrumb-item + .breadcrumb-item { + padding-left: 0.5rem; +} + +.breadcrumb-item + .breadcrumb-item::before { + display: inline-block; + padding-right: 0.5rem; + color: #6c757d; + content: "/"; +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: underline; +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: none; +} + +.breadcrumb-item.active { + color: #6c757d; +} + +.pagination { + display: -ms-flexbox; + display: flex; + padding-left: 0; + list-style: none; + border-radius: 0.25rem; +} + +.page-link { + position: relative; + display: block; + padding: 0.5rem 0.75rem; + margin-left: -1px; + line-height: 1.25; + color: #007bff; + background-color: #fff; + border: 1px solid #dee2e6; +} + +.page-link:hover { + z-index: 2; + color: #0056b3; + text-decoration: none; + background-color: #e9ecef; + border-color: #dee2e6; +} + +.page-link:focus { + z-index: 2; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.page-item:last-child .page-link { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; +} + +.page-item.active .page-link { + z-index: 1; + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.page-item.disabled .page-link { + color: #6c757d; + pointer-events: none; + cursor: auto; + background-color: #fff; + border-color: #dee2e6; +} + +.pagination-lg .page-link { + padding: 0.75rem 1.5rem; + font-size: 1.25rem; + line-height: 1.5; +} + +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} + +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: 0.3rem; + border-bottom-right-radius: 0.3rem; +} + +.pagination-sm .page-link { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; +} + +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; +} + +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: 0.2rem; + border-bottom-right-radius: 0.2rem; +} + +.badge { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .badge { + transition: none; + } +} + +a.badge:hover, a.badge:focus { + text-decoration: none; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +.badge-pill { + padding-right: 0.6em; + padding-left: 0.6em; + border-radius: 10rem; +} + +.badge-primary { + color: #fff; + background-color: #007bff; +} + +a.badge-primary:hover, a.badge-primary:focus { + color: #fff; + background-color: #0062cc; +} + +a.badge-primary:focus, a.badge-primary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.badge-secondary { + color: #fff; + background-color: #6c757d; +} + +a.badge-secondary:hover, a.badge-secondary:focus { + color: #fff; + background-color: #545b62; +} + +a.badge-secondary:focus, a.badge-secondary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.badge-success { + color: #fff; + background-color: #28a745; +} + +a.badge-success:hover, a.badge-success:focus { + color: #fff; + background-color: #1e7e34; +} + +a.badge-success:focus, a.badge-success.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.badge-info { + color: #fff; + background-color: #17a2b8; +} + +a.badge-info:hover, a.badge-info:focus { + color: #fff; + background-color: #117a8b; +} + +a.badge-info:focus, a.badge-info.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.badge-warning { + color: #212529; + background-color: #ffc107; +} + +a.badge-warning:hover, a.badge-warning:focus { + color: #212529; + background-color: #d39e00; +} + +a.badge-warning:focus, a.badge-warning.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.badge-danger { + color: #fff; + background-color: #dc3545; +} + +a.badge-danger:hover, a.badge-danger:focus { + color: #fff; + background-color: #bd2130; +} + +a.badge-danger:focus, a.badge-danger.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.badge-light { + color: #212529; + background-color: #f8f9fa; +} + +a.badge-light:hover, a.badge-light:focus { + color: #212529; + background-color: #dae0e5; +} + +a.badge-light:focus, a.badge-light.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.badge-dark { + color: #fff; + background-color: #343a40; +} + +a.badge-dark:hover, a.badge-dark:focus { + color: #fff; + background-color: #1d2124; +} + +a.badge-dark:focus, a.badge-dark.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #e9ecef; + border-radius: 0.3rem; +} + +@media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem; + } +} + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0; +} + +.alert { + position: relative; + padding: 0.75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: 0.25rem; +} + +.alert-heading { + color: inherit; +} + +.alert-link { + font-weight: 700; +} + +.alert-dismissible { + padding-right: 4rem; +} + +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: 0.75rem 1.25rem; + color: inherit; +} + +.alert-primary { + color: #004085; + background-color: #cce5ff; + border-color: #b8daff; +} + +.alert-primary hr { + border-top-color: #9fcdff; +} + +.alert-primary .alert-link { + color: #002752; +} + +.alert-secondary { + color: #383d41; + background-color: #e2e3e5; + border-color: #d6d8db; +} + +.alert-secondary hr { + border-top-color: #c8cbcf; +} + +.alert-secondary .alert-link { + color: #202326; +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; +} + +.alert-success hr { + border-top-color: #b1dfbb; +} + +.alert-success .alert-link { + color: #0b2e13; +} + +.alert-info { + color: #0c5460; + background-color: #d1ecf1; + border-color: #bee5eb; +} + +.alert-info hr { + border-top-color: #abdde5; +} + +.alert-info .alert-link { + color: #062c33; +} + +.alert-warning { + color: #856404; + background-color: #fff3cd; + border-color: #ffeeba; +} + +.alert-warning hr { + border-top-color: #ffe8a1; +} + +.alert-warning .alert-link { + color: #533f03; +} + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} + +.alert-danger hr { + border-top-color: #f1b0b7; +} + +.alert-danger .alert-link { + color: #491217; +} + +.alert-light { + color: #818182; + background-color: #fefefe; + border-color: #fdfdfe; +} + +.alert-light hr { + border-top-color: #ececf6; +} + +.alert-light .alert-link { + color: #686868; +} + +.alert-dark { + color: #1b1e21; + background-color: #d6d8d9; + border-color: #c6c8ca; +} + +.alert-dark hr { + border-top-color: #b9bbbe; +} + +.alert-dark .alert-link { + color: #040505; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 1rem 0; + } + to { + background-position: 0 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 1rem 0; + } + to { + background-position: 0 0; + } +} + +.progress { + display: -ms-flexbox; + display: flex; + height: 1rem; + overflow: hidden; + font-size: 0.75rem; + background-color: #e9ecef; + border-radius: 0.25rem; +} + +.progress-bar { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #007bff; + transition: width 0.6s ease; +} + +@media (prefers-reduced-motion: reduce) { + .progress-bar { + transition: none; + } +} + +.progress-bar-striped { + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 1rem 1rem; +} + +.progress-bar-animated { + -webkit-animation: progress-bar-stripes 1s linear infinite; + animation: progress-bar-stripes 1s linear infinite; +} + +@media (prefers-reduced-motion: reduce) { + .progress-bar-animated { + -webkit-animation: none; + animation: none; + } +} + +.media { + display: -ms-flexbox; + display: flex; + -ms-flex-align: start; + align-items: flex-start; +} + +.media-body { + -ms-flex: 1; + flex: 1; +} + +.list-group { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; +} + +.list-group-item-action { + width: 100%; + color: #495057; + text-align: inherit; +} + +.list-group-item-action:hover, .list-group-item-action:focus { + z-index: 1; + color: #495057; + text-decoration: none; + background-color: #f8f9fa; +} + +.list-group-item-action:active { + color: #212529; + background-color: #e9ecef; +} + +.list-group-item { + position: relative; + display: block; + padding: 0.75rem 1.25rem; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.125); +} + +.list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: #fff; +} + +.list-group-item.active { + z-index: 2; + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.list-group-horizontal { + -ms-flex-direction: row; + flex-direction: row; +} + +.list-group-horizontal .list-group-item { + margin-right: -1px; + margin-bottom: 0; +} + +.list-group-horizontal .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; +} + +.list-group-horizontal .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; +} + +@media (min-width: 576px) { + .list-group-horizontal-sm { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-sm .list-group-item { + margin-right: -1px; + margin-bottom: 0; + } + .list-group-horizontal-sm .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-sm .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } +} + +@media (min-width: 768px) { + .list-group-horizontal-md { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-md .list-group-item { + margin-right: -1px; + margin-bottom: 0; + } + .list-group-horizontal-md .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-md .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } +} + +@media (min-width: 992px) { + .list-group-horizontal-lg { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-lg .list-group-item { + margin-right: -1px; + margin-bottom: 0; + } + .list-group-horizontal-lg .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-lg .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } +} + +@media (min-width: 1200px) { + .list-group-horizontal-xl { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-xl .list-group-item { + margin-right: -1px; + margin-bottom: 0; + } + .list-group-horizontal-xl .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-xl .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } +} + +.list-group-flush .list-group-item { + border-right: 0; + border-left: 0; + border-radius: 0; +} + +.list-group-flush .list-group-item:last-child { + margin-bottom: -1px; +} + +.list-group-flush:first-child .list-group-item:first-child { + border-top: 0; +} + +.list-group-flush:last-child .list-group-item:last-child { + margin-bottom: 0; + border-bottom: 0; +} + +.list-group-item-primary { + color: #004085; + background-color: #b8daff; +} + +.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { + color: #004085; + background-color: #9fcdff; +} + +.list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #004085; + border-color: #004085; +} + +.list-group-item-secondary { + color: #383d41; + background-color: #d6d8db; +} + +.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { + color: #383d41; + background-color: #c8cbcf; +} + +.list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #383d41; + border-color: #383d41; +} + +.list-group-item-success { + color: #155724; + background-color: #c3e6cb; +} + +.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { + color: #155724; + background-color: #b1dfbb; +} + +.list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #155724; + border-color: #155724; +} + +.list-group-item-info { + color: #0c5460; + background-color: #bee5eb; +} + +.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { + color: #0c5460; + background-color: #abdde5; +} + +.list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #0c5460; + border-color: #0c5460; +} + +.list-group-item-warning { + color: #856404; + background-color: #ffeeba; +} + +.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { + color: #856404; + background-color: #ffe8a1; +} + +.list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #856404; + border-color: #856404; +} + +.list-group-item-danger { + color: #721c24; + background-color: #f5c6cb; +} + +.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { + color: #721c24; + background-color: #f1b0b7; +} + +.list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #721c24; + border-color: #721c24; +} + +.list-group-item-light { + color: #818182; + background-color: #fdfdfe; +} + +.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { + color: #818182; + background-color: #ececf6; +} + +.list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #818182; + border-color: #818182; +} + +.list-group-item-dark { + color: #1b1e21; + background-color: #c6c8ca; +} + +.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { + color: #1b1e21; + background-color: #b9bbbe; +} + +.list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #1b1e21; + border-color: #1b1e21; +} + +.close { + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5; +} + +.close:hover { + color: #000; + text-decoration: none; +} + +.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { + opacity: .75; +} + +button.close { + padding: 0; + background-color: transparent; + border: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +a.close.disabled { + pointer-events: none; +} + +.toast { + max-width: 350px; + overflow: hidden; + font-size: 0.875rem; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + opacity: 0; + border-radius: 0.25rem; +} + +.toast:not(:last-child) { + margin-bottom: 0.75rem; +} + +.toast.showing { + opacity: 1; +} + +.toast.show { + display: block; + opacity: 1; +} + +.toast.hide { + display: none; +} + +.toast-header { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + padding: 0.25rem 0.75rem; + color: #6c757d; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); +} + +.toast-body { + padding: 0.75rem; +} + +.modal-open { + overflow: hidden; +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +.modal { + position: fixed; + top: 0; + left: 0; + z-index: 1050; + display: none; + width: 100%; + height: 100%; + overflow: hidden; + outline: 0; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 0.5rem; + pointer-events: none; +} + +.modal.fade .modal-dialog { + transition: -webkit-transform 0.3s ease-out; + transition: transform 0.3s ease-out; + transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out; + -webkit-transform: translate(0, -50px); + transform: translate(0, -50px); +} + +@media (prefers-reduced-motion: reduce) { + .modal.fade .modal-dialog { + transition: none; + } +} + +.modal.show .modal-dialog { + -webkit-transform: none; + transform: none; +} + +.modal-dialog-scrollable { + display: -ms-flexbox; + display: flex; + max-height: calc(100% - 1rem); +} + +.modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 1rem); + overflow: hidden; +} + +.modal-dialog-scrollable .modal-header, +.modal-dialog-scrollable .modal-footer { + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.modal-dialog-scrollable .modal-body { + overflow-y: auto; +} + +.modal-dialog-centered { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + min-height: calc(100% - 1rem); +} + +.modal-dialog-centered::before { + display: block; + height: calc(100vh - 1rem); + content: ""; +} + +.modal-dialog-centered.modal-dialog-scrollable { + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + height: 100%; +} + +.modal-dialog-centered.modal-dialog-scrollable .modal-content { + max-height: none; +} + +.modal-dialog-centered.modal-dialog-scrollable::before { + content: none; +} + +.modal-content { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; + outline: 0; +} + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop.show { + opacity: 0.5; +} + +.modal-header { + display: -ms-flexbox; + display: flex; + -ms-flex-align: start; + align-items: flex-start; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 1rem 1rem; + border-bottom: 1px solid #dee2e6; + border-top-left-radius: 0.3rem; + border-top-right-radius: 0.3rem; +} + +.modal-header .close { + padding: 1rem 1rem; + margin: -1rem -1rem -1rem auto; +} + +.modal-title { + margin-bottom: 0; + line-height: 1.5; +} + +.modal-body { + position: relative; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + padding: 1rem; +} + +.modal-footer { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: end; + justify-content: flex-end; + padding: 1rem; + border-top: 1px solid #dee2e6; + border-bottom-right-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} + +.modal-footer > :not(:first-child) { + margin-left: .25rem; +} + +.modal-footer > :not(:last-child) { + margin-right: .25rem; +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto; + } + .modal-dialog-scrollable { + max-height: calc(100% - 3.5rem); + } + .modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 3.5rem); + } + .modal-dialog-centered { + min-height: calc(100% - 3.5rem); + } + .modal-dialog-centered::before { + height: calc(100vh - 3.5rem); + } + .modal-sm { + max-width: 300px; + } +} + +@media (min-width: 992px) { + .modal-lg, + .modal-xl { + max-width: 800px; + } +} + +@media (min-width: 1200px) { + .modal-xl { + max-width: 1140px; + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + opacity: 0; +} + +.tooltip.show { + opacity: 0.9; +} + +.tooltip .arrow { + position: absolute; + display: block; + width: 0.8rem; + height: 0.4rem; +} + +.tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { + padding: 0.4rem 0; +} + +.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { + bottom: 0; +} + +.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { + top: 0; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; +} + +.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { + padding: 0 0.4rem; +} + +.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { + right: 0; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; +} + +.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { + padding: 0.4rem 0; +} + +.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { + top: 0; +} + +.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { + bottom: 0; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; +} + +.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { + padding: 0 0.4rem; +} + +.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { + left: 0; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; +} + +.tooltip-inner { + max-width: 200px; + padding: 0.25rem 0.5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 0.25rem; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; +} + +.popover .arrow { + position: absolute; + display: block; + width: 1rem; + height: 0.5rem; + margin: 0 0.3rem; +} + +.popover .arrow::before, .popover .arrow::after { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-popover-top, .bs-popover-auto[x-placement^="top"] { + margin-bottom: 0.5rem; +} + +.bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { + bottom: calc((0.5rem + 1px) * -1); +} + +.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { + bottom: 0; + border-width: 0.5rem 0.5rem 0; + border-top-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^="top"] > .arrow::after { + bottom: 1px; + border-width: 0.5rem 0.5rem 0; + border-top-color: #fff; +} + +.bs-popover-right, .bs-popover-auto[x-placement^="right"] { + margin-left: 0.5rem; +} + +.bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { + left: calc((0.5rem + 1px) * -1); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} + +.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^="right"] > .arrow::before { + left: 0; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^="right"] > .arrow::after { + left: 1px; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: #fff; +} + +.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { + margin-top: 0.5rem; +} + +.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { + top: calc((0.5rem + 1px) * -1); +} + +.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { + top: 0; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after { + top: 1px; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: #fff; +} + +.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7; +} + +.bs-popover-left, .bs-popover-auto[x-placement^="left"] { + margin-right: 0.5rem; +} + +.bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { + right: calc((0.5rem + 1px) * -1); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} + +.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^="left"] > .arrow::before { + right: 0; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^="left"] > .arrow::after { + right: 1px; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: #fff; +} + +.popover-header { + padding: 0.5rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} + +.popover-header:empty { + display: none; +} + +.popover-body { + padding: 0.5rem 0.75rem; + color: #212529; +} + +.carousel { + position: relative; +} + +.carousel.pointer-event { + -ms-touch-action: pan-y; + touch-action: pan-y; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner::after { + display: block; + clear: both; + content: ""; +} + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transition: -webkit-transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .carousel-item { + transition: none; + } +} + +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; +} + +.carousel-item-next:not(.carousel-item-left), +.active.carousel-item-right { + -webkit-transform: translateX(100%); + transform: translateX(100%); +} + +.carousel-item-prev:not(.carousel-item-right), +.active.carousel-item-left { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); +} + +.carousel-fade .carousel-item { + opacity: 0; + transition-property: opacity; + -webkit-transform: none; + transform: none; +} + +.carousel-fade .carousel-item.active, +.carousel-fade .carousel-item-next.carousel-item-left, +.carousel-fade .carousel-item-prev.carousel-item-right { + z-index: 1; + opacity: 1; +} + +.carousel-fade .active.carousel-item-left, +.carousel-fade .active.carousel-item-right { + z-index: 0; + opacity: 0; + transition: 0s 0.6s opacity; +} + +@media (prefers-reduced-motion: reduce) { + .carousel-fade .active.carousel-item-left, + .carousel-fade .active.carousel-item-right { + transition: none; + } +} + +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + width: 15%; + color: #fff; + text-align: center; + opacity: 0.5; + transition: opacity 0.15s ease; +} + +@media (prefers-reduced-motion: reduce) { + .carousel-control-prev, + .carousel-control-next { + transition: none; + } +} + +.carousel-control-prev:hover, .carousel-control-prev:focus, +.carousel-control-next:hover, +.carousel-control-next:focus { + color: #fff; + text-decoration: none; + outline: 0; + opacity: 0.9; +} + +.carousel-control-prev { + left: 0; +} + +.carousel-control-next { + right: 0; +} + +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: 20px; + height: 20px; + background: no-repeat 50% / 100% 100%; +} + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e"); +} + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e"); +} + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 15; + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none; +} + +.carousel-indicators li { + box-sizing: content-box; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: .5; + transition: opacity 0.6s ease; +} + +@media (prefers-reduced-motion: reduce) { + .carousel-indicators li { + transition: none; + } +} + +.carousel-indicators .active { + opacity: 1; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; +} + +@-webkit-keyframes spinner-border { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes spinner-border { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +.spinner-border { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + border: 0.25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + -webkit-animation: spinner-border .75s linear infinite; + animation: spinner-border .75s linear infinite; +} + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: 0.2em; +} + +@-webkit-keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + } + 50% { + opacity: 1; + } +} + +@keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + } + 50% { + opacity: 1; + } +} + +.spinner-grow { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + background-color: currentColor; + border-radius: 50%; + opacity: 0; + -webkit-animation: spinner-grow .75s linear infinite; + animation: spinner-grow .75s linear infinite; +} + +.spinner-grow-sm { + width: 1rem; + height: 1rem; +} + +.align-baseline { + vertical-align: baseline !important; +} + +.align-top { + vertical-align: top !important; +} + +.align-middle { + vertical-align: middle !important; +} + +.align-bottom { + vertical-align: bottom !important; +} + +.align-text-bottom { + vertical-align: text-bottom !important; +} + +.align-text-top { + vertical-align: text-top !important; +} + +.bg-primary { + background-color: #007bff !important; +} + +a.bg-primary:hover, a.bg-primary:focus, +button.bg-primary:hover, +button.bg-primary:focus { + background-color: #0062cc !important; +} + +.bg-secondary { + background-color: #6c757d !important; +} + +a.bg-secondary:hover, a.bg-secondary:focus, +button.bg-secondary:hover, +button.bg-secondary:focus { + background-color: #545b62 !important; +} + +.bg-success { + background-color: #28a745 !important; +} + +a.bg-success:hover, a.bg-success:focus, +button.bg-success:hover, +button.bg-success:focus { + background-color: #1e7e34 !important; +} + +.bg-info { + background-color: #17a2b8 !important; +} + +a.bg-info:hover, a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #117a8b !important; +} + +.bg-warning { + background-color: #ffc107 !important; +} + +a.bg-warning:hover, a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #d39e00 !important; +} + +.bg-danger { + background-color: #dc3545 !important; +} + +a.bg-danger:hover, a.bg-danger:focus, +button.bg-danger:hover, +button.bg-danger:focus { + background-color: #bd2130 !important; +} + +.bg-light { + background-color: #f8f9fa !important; +} + +a.bg-light:hover, a.bg-light:focus, +button.bg-light:hover, +button.bg-light:focus { + background-color: #dae0e5 !important; +} + +.bg-dark { + background-color: #343a40 !important; +} + +a.bg-dark:hover, a.bg-dark:focus, +button.bg-dark:hover, +button.bg-dark:focus { + background-color: #1d2124 !important; +} + +.bg-white { + background-color: #fff !important; +} + +.bg-transparent { + background-color: transparent !important; +} + +.border { + border: 1px solid #dee2e6 !important; +} + +.border-top { + border-top: 1px solid #dee2e6 !important; +} + +.border-right { + border-right: 1px solid #dee2e6 !important; +} + +.border-bottom { + border-bottom: 1px solid #dee2e6 !important; +} + +.border-left { + border-left: 1px solid #dee2e6 !important; +} + +.border-0 { + border: 0 !important; +} + +.border-top-0 { + border-top: 0 !important; +} + +.border-right-0 { + border-right: 0 !important; +} + +.border-bottom-0 { + border-bottom: 0 !important; +} + +.border-left-0 { + border-left: 0 !important; +} + +.border-primary { + border-color: #007bff !important; +} + +.border-secondary { + border-color: #6c757d !important; +} + +.border-success { + border-color: #28a745 !important; +} + +.border-info { + border-color: #17a2b8 !important; +} + +.border-warning { + border-color: #ffc107 !important; +} + +.border-danger { + border-color: #dc3545 !important; +} + +.border-light { + border-color: #f8f9fa !important; +} + +.border-dark { + border-color: #343a40 !important; +} + +.border-white { + border-color: #fff !important; +} + +.rounded-sm { + border-radius: 0.2rem !important; +} + +.rounded { + border-radius: 0.25rem !important; +} + +.rounded-top { + border-top-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; +} + +.rounded-right { + border-top-right-radius: 0.25rem !important; + border-bottom-right-radius: 0.25rem !important; +} + +.rounded-bottom { + border-bottom-right-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; +} + +.rounded-left { + border-top-left-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; +} + +.rounded-lg { + border-radius: 0.3rem !important; +} + +.rounded-circle { + border-radius: 50% !important; +} + +.rounded-pill { + border-radius: 50rem !important; +} + +.rounded-0 { + border-radius: 0 !important; +} + +.clearfix::after { + display: block; + clear: both; + content: ""; +} + +.d-none { + display: none !important; +} + +.d-inline { + display: inline !important; +} + +.d-inline-block { + display: inline-block !important; +} + +.d-block { + display: block !important; +} + +.d-table { + display: table !important; +} + +.d-table-row { + display: table-row !important; +} + +.d-table-cell { + display: table-cell !important; +} + +.d-flex { + display: -ms-flexbox !important; + display: flex !important; +} + +.d-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; +} + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; + } + .d-sm-inline { + display: inline !important; + } + .d-sm-inline-block { + display: inline-block !important; + } + .d-sm-block { + display: block !important; + } + .d-sm-table { + display: table !important; + } + .d-sm-table-row { + display: table-row !important; + } + .d-sm-table-cell { + display: table-cell !important; + } + .d-sm-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-sm-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 768px) { + .d-md-none { + display: none !important; + } + .d-md-inline { + display: inline !important; + } + .d-md-inline-block { + display: inline-block !important; + } + .d-md-block { + display: block !important; + } + .d-md-table { + display: table !important; + } + .d-md-table-row { + display: table-row !important; + } + .d-md-table-cell { + display: table-cell !important; + } + .d-md-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-md-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 992px) { + .d-lg-none { + display: none !important; + } + .d-lg-inline { + display: inline !important; + } + .d-lg-inline-block { + display: inline-block !important; + } + .d-lg-block { + display: block !important; + } + .d-lg-table { + display: table !important; + } + .d-lg-table-row { + display: table-row !important; + } + .d-lg-table-cell { + display: table-cell !important; + } + .d-lg-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-lg-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 1200px) { + .d-xl-none { + display: none !important; + } + .d-xl-inline { + display: inline !important; + } + .d-xl-inline-block { + display: inline-block !important; + } + .d-xl-block { + display: block !important; + } + .d-xl-table { + display: table !important; + } + .d-xl-table-row { + display: table-row !important; + } + .d-xl-table-cell { + display: table-cell !important; + } + .d-xl-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-xl-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media print { + .d-print-none { + display: none !important; + } + .d-print-inline { + display: inline !important; + } + .d-print-inline-block { + display: inline-block !important; + } + .d-print-block { + display: block !important; + } + .d-print-table { + display: table !important; + } + .d-print-table-row { + display: table-row !important; + } + .d-print-table-cell { + display: table-cell !important; + } + .d-print-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-print-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden; +} + +.embed-responsive::before { + display: block; + content: ""; +} + +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +.embed-responsive-21by9::before { + padding-top: 42.857143%; +} + +.embed-responsive-16by9::before { + padding-top: 56.25%; +} + +.embed-responsive-4by3::before { + padding-top: 75%; +} + +.embed-responsive-1by1::before { + padding-top: 100%; +} + +.flex-row { + -ms-flex-direction: row !important; + flex-direction: row !important; +} + +.flex-column { + -ms-flex-direction: column !important; + flex-direction: column !important; +} + +.flex-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; +} + +.flex-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; +} + +.flex-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; +} + +.flex-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; +} + +.flex-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; +} + +.flex-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; +} + +.flex-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; +} + +.flex-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; +} + +.flex-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; +} + +.flex-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; +} + +.justify-content-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; +} + +.justify-content-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; +} + +.justify-content-center { + -ms-flex-pack: center !important; + justify-content: center !important; +} + +.justify-content-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; +} + +.justify-content-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; +} + +.align-items-start { + -ms-flex-align: start !important; + align-items: flex-start !important; +} + +.align-items-end { + -ms-flex-align: end !important; + align-items: flex-end !important; +} + +.align-items-center { + -ms-flex-align: center !important; + align-items: center !important; +} + +.align-items-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; +} + +.align-items-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; +} + +.align-content-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; +} + +.align-content-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; +} + +.align-content-center { + -ms-flex-line-pack: center !important; + align-content: center !important; +} + +.align-content-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; +} + +.align-content-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; +} + +.align-content-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; +} + +.align-self-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; +} + +.align-self-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; +} + +.align-self-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; +} + +.align-self-center { + -ms-flex-item-align: center !important; + align-self: center !important; +} + +.align-self-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; +} + +.align-self-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; +} + +@media (min-width: 576px) { + .flex-sm-row { + -ms-flex-direction: row !important; + flex-direction: row !important; + } + .flex-sm-column { + -ms-flex-direction: column !important; + flex-direction: column !important; + } + .flex-sm-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + .flex-sm-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + .flex-sm-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + .flex-sm-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + .flex-sm-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + .flex-sm-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + .flex-sm-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + .flex-sm-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + .flex-sm-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + .flex-sm-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + .justify-content-sm-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + .justify-content-sm-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + .justify-content-sm-center { + -ms-flex-pack: center !important; + justify-content: center !important; + } + .justify-content-sm-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + .justify-content-sm-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + .align-items-sm-start { + -ms-flex-align: start !important; + align-items: flex-start !important; + } + .align-items-sm-end { + -ms-flex-align: end !important; + align-items: flex-end !important; + } + .align-items-sm-center { + -ms-flex-align: center !important; + align-items: center !important; + } + .align-items-sm-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + .align-items-sm-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + .align-content-sm-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + .align-content-sm-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + .align-content-sm-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + .align-content-sm-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + .align-content-sm-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + .align-content-sm-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + .align-self-sm-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + .align-self-sm-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + .align-self-sm-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + .align-self-sm-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + .align-self-sm-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + .align-self-sm-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 768px) { + .flex-md-row { + -ms-flex-direction: row !important; + flex-direction: row !important; + } + .flex-md-column { + -ms-flex-direction: column !important; + flex-direction: column !important; + } + .flex-md-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + .flex-md-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + .flex-md-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + .flex-md-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + .flex-md-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + .flex-md-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + .flex-md-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + .flex-md-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + .flex-md-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + .flex-md-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + .justify-content-md-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + .justify-content-md-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + .justify-content-md-center { + -ms-flex-pack: center !important; + justify-content: center !important; + } + .justify-content-md-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + .justify-content-md-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + .align-items-md-start { + -ms-flex-align: start !important; + align-items: flex-start !important; + } + .align-items-md-end { + -ms-flex-align: end !important; + align-items: flex-end !important; + } + .align-items-md-center { + -ms-flex-align: center !important; + align-items: center !important; + } + .align-items-md-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + .align-items-md-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + .align-content-md-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + .align-content-md-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + .align-content-md-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + .align-content-md-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + .align-content-md-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + .align-content-md-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + .align-self-md-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + .align-self-md-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + .align-self-md-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + .align-self-md-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + .align-self-md-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + .align-self-md-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 992px) { + .flex-lg-row { + -ms-flex-direction: row !important; + flex-direction: row !important; + } + .flex-lg-column { + -ms-flex-direction: column !important; + flex-direction: column !important; + } + .flex-lg-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + .flex-lg-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + .flex-lg-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + .flex-lg-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + .flex-lg-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + .flex-lg-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + .flex-lg-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + .flex-lg-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + .flex-lg-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + .flex-lg-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + .justify-content-lg-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + .justify-content-lg-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + .justify-content-lg-center { + -ms-flex-pack: center !important; + justify-content: center !important; + } + .justify-content-lg-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + .justify-content-lg-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + .align-items-lg-start { + -ms-flex-align: start !important; + align-items: flex-start !important; + } + .align-items-lg-end { + -ms-flex-align: end !important; + align-items: flex-end !important; + } + .align-items-lg-center { + -ms-flex-align: center !important; + align-items: center !important; + } + .align-items-lg-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + .align-items-lg-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + .align-content-lg-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + .align-content-lg-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + .align-content-lg-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + .align-content-lg-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + .align-content-lg-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + .align-content-lg-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + .align-self-lg-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + .align-self-lg-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + .align-self-lg-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + .align-self-lg-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + .align-self-lg-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + .align-self-lg-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 1200px) { + .flex-xl-row { + -ms-flex-direction: row !important; + flex-direction: row !important; + } + .flex-xl-column { + -ms-flex-direction: column !important; + flex-direction: column !important; + } + .flex-xl-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + .flex-xl-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + .flex-xl-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + .flex-xl-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + .flex-xl-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + .flex-xl-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + .flex-xl-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + .flex-xl-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + .flex-xl-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + .flex-xl-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + .justify-content-xl-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + .justify-content-xl-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + .justify-content-xl-center { + -ms-flex-pack: center !important; + justify-content: center !important; + } + .justify-content-xl-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + .justify-content-xl-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + .align-items-xl-start { + -ms-flex-align: start !important; + align-items: flex-start !important; + } + .align-items-xl-end { + -ms-flex-align: end !important; + align-items: flex-end !important; + } + .align-items-xl-center { + -ms-flex-align: center !important; + align-items: center !important; + } + .align-items-xl-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + .align-items-xl-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + .align-content-xl-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + .align-content-xl-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + .align-content-xl-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + .align-content-xl-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + .align-content-xl-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + .align-content-xl-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + .align-self-xl-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + .align-self-xl-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + .align-self-xl-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + .align-self-xl-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + .align-self-xl-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + .align-self-xl-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +.float-left { + float: left !important; +} + +.float-right { + float: right !important; +} + +.float-none { + float: none !important; +} + +@media (min-width: 576px) { + .float-sm-left { + float: left !important; + } + .float-sm-right { + float: right !important; + } + .float-sm-none { + float: none !important; + } +} + +@media (min-width: 768px) { + .float-md-left { + float: left !important; + } + .float-md-right { + float: right !important; + } + .float-md-none { + float: none !important; + } +} + +@media (min-width: 992px) { + .float-lg-left { + float: left !important; + } + .float-lg-right { + float: right !important; + } + .float-lg-none { + float: none !important; + } +} + +@media (min-width: 1200px) { + .float-xl-left { + float: left !important; + } + .float-xl-right { + float: right !important; + } + .float-xl-none { + float: none !important; + } +} + +.overflow-auto { + overflow: auto !important; +} + +.overflow-hidden { + overflow: hidden !important; +} + +.position-static { + position: static !important; +} + +.position-relative { + position: relative !important; +} + +.position-absolute { + position: absolute !important; +} + +.position-fixed { + position: fixed !important; +} + +.position-sticky { + position: -webkit-sticky !important; + position: sticky !important; +} + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; +} + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; +} + +@supports ((position: -webkit-sticky) or (position: sticky)) { + .sticky-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal; +} + +.shadow-sm { + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; +} + +.shadow { + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; +} + +.shadow-lg { + box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; +} + +.shadow-none { + box-shadow: none !important; +} + +.w-25 { + width: 25% !important; +} + +.w-50 { + width: 50% !important; +} + +.w-75 { + width: 75% !important; +} + +.w-100 { + width: 100% !important; +} + +.w-auto { + width: auto !important; +} + +.h-25 { + height: 25% !important; +} + +.h-50 { + height: 50% !important; +} + +.h-75 { + height: 75% !important; +} + +.h-100 { + height: 100% !important; +} + +.h-auto { + height: auto !important; +} + +.mw-100 { + max-width: 100% !important; +} + +.mh-100 { + max-height: 100% !important; +} + +.min-vw-100 { + min-width: 100vw !important; +} + +.min-vh-100 { + min-height: 100vh !important; +} + +.vw-100 { + width: 100vw !important; +} + +.vh-100 { + height: 100vh !important; +} + +.stretched-link::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + pointer-events: auto; + content: ""; + background-color: rgba(0, 0, 0, 0); +} + +.m-0 { + margin: 0 !important; +} + +.mt-0, +.my-0 { + margin-top: 0 !important; +} + +.mr-0, +.mx-0 { + margin-right: 0 !important; +} + +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} + +.ml-0, +.mx-0 { + margin-left: 0 !important; +} + +.m-1 { + margin: 0.25rem !important; +} + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; +} + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; +} + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} + +.m-2 { + margin: 0.5rem !important; +} + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} + +.m-3 { + margin: 1rem !important; +} + +.mt-3, +.my-3 { + margin-top: 1rem !important; +} + +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} + +.mb-3, +.my-3 { + margin-bottom: 1rem !important; +} + +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} + +.m-4 { + margin: 1.5rem !important; +} + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; +} + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} + +.m-5 { + margin: 3rem !important; +} + +.mt-5, +.my-5 { + margin-top: 3rem !important; +} + +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} + +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} + +.p-0 { + padding: 0 !important; +} + +.pt-0, +.py-0 { + padding-top: 0 !important; +} + +.pr-0, +.px-0 { + padding-right: 0 !important; +} + +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} + +.pl-0, +.px-0 { + padding-left: 0 !important; +} + +.p-1 { + padding: 0.25rem !important; +} + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} + +.p-2 { + padding: 0.5rem !important; +} + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} + +.p-3 { + padding: 1rem !important; +} + +.pt-3, +.py-3 { + padding-top: 1rem !important; +} + +.pr-3, +.px-3 { + padding-right: 1rem !important; +} + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} + +.pl-3, +.px-3 { + padding-left: 1rem !important; +} + +.p-4 { + padding: 1.5rem !important; +} + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} + +.p-5 { + padding: 3rem !important; +} + +.pt-5, +.py-5 { + padding-top: 3rem !important; +} + +.pr-5, +.px-5 { + padding-right: 3rem !important; +} + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} + +.pl-5, +.px-5 { + padding-left: 3rem !important; +} + +.m-n1 { + margin: -0.25rem !important; +} + +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; +} + +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; +} + +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; +} + +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; +} + +.m-n2 { + margin: -0.5rem !important; +} + +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; +} + +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; +} + +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; +} + +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; +} + +.m-n3 { + margin: -1rem !important; +} + +.mt-n3, +.my-n3 { + margin-top: -1rem !important; +} + +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; +} + +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; +} + +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; +} + +.m-n4 { + margin: -1.5rem !important; +} + +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; +} + +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; +} + +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; +} + +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; +} + +.m-n5 { + margin: -3rem !important; +} + +.mt-n5, +.my-n5 { + margin-top: -3rem !important; +} + +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; +} + +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; +} + +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; +} + +.m-auto { + margin: auto !important; +} + +.mt-auto, +.my-auto { + margin-top: auto !important; +} + +.mr-auto, +.mx-auto { + margin-right: auto !important; +} + +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} + +.ml-auto, +.mx-auto { + margin-left: auto !important; +} + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; + } + .mt-sm-0, + .my-sm-0 { + margin-top: 0 !important; + } + .mr-sm-0, + .mx-sm-0 { + margin-right: 0 !important; + } + .mb-sm-0, + .my-sm-0 { + margin-bottom: 0 !important; + } + .ml-sm-0, + .mx-sm-0 { + margin-left: 0 !important; + } + .m-sm-1 { + margin: 0.25rem !important; + } + .mt-sm-1, + .my-sm-1 { + margin-top: 0.25rem !important; + } + .mr-sm-1, + .mx-sm-1 { + margin-right: 0.25rem !important; + } + .mb-sm-1, + .my-sm-1 { + margin-bottom: 0.25rem !important; + } + .ml-sm-1, + .mx-sm-1 { + margin-left: 0.25rem !important; + } + .m-sm-2 { + margin: 0.5rem !important; + } + .mt-sm-2, + .my-sm-2 { + margin-top: 0.5rem !important; + } + .mr-sm-2, + .mx-sm-2 { + margin-right: 0.5rem !important; + } + .mb-sm-2, + .my-sm-2 { + margin-bottom: 0.5rem !important; + } + .ml-sm-2, + .mx-sm-2 { + margin-left: 0.5rem !important; + } + .m-sm-3 { + margin: 1rem !important; + } + .mt-sm-3, + .my-sm-3 { + margin-top: 1rem !important; + } + .mr-sm-3, + .mx-sm-3 { + margin-right: 1rem !important; + } + .mb-sm-3, + .my-sm-3 { + margin-bottom: 1rem !important; + } + .ml-sm-3, + .mx-sm-3 { + margin-left: 1rem !important; + } + .m-sm-4 { + margin: 1.5rem !important; + } + .mt-sm-4, + .my-sm-4 { + margin-top: 1.5rem !important; + } + .mr-sm-4, + .mx-sm-4 { + margin-right: 1.5rem !important; + } + .mb-sm-4, + .my-sm-4 { + margin-bottom: 1.5rem !important; + } + .ml-sm-4, + .mx-sm-4 { + margin-left: 1.5rem !important; + } + .m-sm-5 { + margin: 3rem !important; + } + .mt-sm-5, + .my-sm-5 { + margin-top: 3rem !important; + } + .mr-sm-5, + .mx-sm-5 { + margin-right: 3rem !important; + } + .mb-sm-5, + .my-sm-5 { + margin-bottom: 3rem !important; + } + .ml-sm-5, + .mx-sm-5 { + margin-left: 3rem !important; + } + .p-sm-0 { + padding: 0 !important; + } + .pt-sm-0, + .py-sm-0 { + padding-top: 0 !important; + } + .pr-sm-0, + .px-sm-0 { + padding-right: 0 !important; + } + .pb-sm-0, + .py-sm-0 { + padding-bottom: 0 !important; + } + .pl-sm-0, + .px-sm-0 { + padding-left: 0 !important; + } + .p-sm-1 { + padding: 0.25rem !important; + } + .pt-sm-1, + .py-sm-1 { + padding-top: 0.25rem !important; + } + .pr-sm-1, + .px-sm-1 { + padding-right: 0.25rem !important; + } + .pb-sm-1, + .py-sm-1 { + padding-bottom: 0.25rem !important; + } + .pl-sm-1, + .px-sm-1 { + padding-left: 0.25rem !important; + } + .p-sm-2 { + padding: 0.5rem !important; + } + .pt-sm-2, + .py-sm-2 { + padding-top: 0.5rem !important; + } + .pr-sm-2, + .px-sm-2 { + padding-right: 0.5rem !important; + } + .pb-sm-2, + .py-sm-2 { + padding-bottom: 0.5rem !important; + } + .pl-sm-2, + .px-sm-2 { + padding-left: 0.5rem !important; + } + .p-sm-3 { + padding: 1rem !important; + } + .pt-sm-3, + .py-sm-3 { + padding-top: 1rem !important; + } + .pr-sm-3, + .px-sm-3 { + padding-right: 1rem !important; + } + .pb-sm-3, + .py-sm-3 { + padding-bottom: 1rem !important; + } + .pl-sm-3, + .px-sm-3 { + padding-left: 1rem !important; + } + .p-sm-4 { + padding: 1.5rem !important; + } + .pt-sm-4, + .py-sm-4 { + padding-top: 1.5rem !important; + } + .pr-sm-4, + .px-sm-4 { + padding-right: 1.5rem !important; + } + .pb-sm-4, + .py-sm-4 { + padding-bottom: 1.5rem !important; + } + .pl-sm-4, + .px-sm-4 { + padding-left: 1.5rem !important; + } + .p-sm-5 { + padding: 3rem !important; + } + .pt-sm-5, + .py-sm-5 { + padding-top: 3rem !important; + } + .pr-sm-5, + .px-sm-5 { + padding-right: 3rem !important; + } + .pb-sm-5, + .py-sm-5 { + padding-bottom: 3rem !important; + } + .pl-sm-5, + .px-sm-5 { + padding-left: 3rem !important; + } + .m-sm-n1 { + margin: -0.25rem !important; + } + .mt-sm-n1, + .my-sm-n1 { + margin-top: -0.25rem !important; + } + .mr-sm-n1, + .mx-sm-n1 { + margin-right: -0.25rem !important; + } + .mb-sm-n1, + .my-sm-n1 { + margin-bottom: -0.25rem !important; + } + .ml-sm-n1, + .mx-sm-n1 { + margin-left: -0.25rem !important; + } + .m-sm-n2 { + margin: -0.5rem !important; + } + .mt-sm-n2, + .my-sm-n2 { + margin-top: -0.5rem !important; + } + .mr-sm-n2, + .mx-sm-n2 { + margin-right: -0.5rem !important; + } + .mb-sm-n2, + .my-sm-n2 { + margin-bottom: -0.5rem !important; + } + .ml-sm-n2, + .mx-sm-n2 { + margin-left: -0.5rem !important; + } + .m-sm-n3 { + margin: -1rem !important; + } + .mt-sm-n3, + .my-sm-n3 { + margin-top: -1rem !important; + } + .mr-sm-n3, + .mx-sm-n3 { + margin-right: -1rem !important; + } + .mb-sm-n3, + .my-sm-n3 { + margin-bottom: -1rem !important; + } + .ml-sm-n3, + .mx-sm-n3 { + margin-left: -1rem !important; + } + .m-sm-n4 { + margin: -1.5rem !important; + } + .mt-sm-n4, + .my-sm-n4 { + margin-top: -1.5rem !important; + } + .mr-sm-n4, + .mx-sm-n4 { + margin-right: -1.5rem !important; + } + .mb-sm-n4, + .my-sm-n4 { + margin-bottom: -1.5rem !important; + } + .ml-sm-n4, + .mx-sm-n4 { + margin-left: -1.5rem !important; + } + .m-sm-n5 { + margin: -3rem !important; + } + .mt-sm-n5, + .my-sm-n5 { + margin-top: -3rem !important; + } + .mr-sm-n5, + .mx-sm-n5 { + margin-right: -3rem !important; + } + .mb-sm-n5, + .my-sm-n5 { + margin-bottom: -3rem !important; + } + .ml-sm-n5, + .mx-sm-n5 { + margin-left: -3rem !important; + } + .m-sm-auto { + margin: auto !important; + } + .mt-sm-auto, + .my-sm-auto { + margin-top: auto !important; + } + .mr-sm-auto, + .mx-sm-auto { + margin-right: auto !important; + } + .mb-sm-auto, + .my-sm-auto { + margin-bottom: auto !important; + } + .ml-sm-auto, + .mx-sm-auto { + margin-left: auto !important; + } +} + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; + } + .mt-md-0, + .my-md-0 { + margin-top: 0 !important; + } + .mr-md-0, + .mx-md-0 { + margin-right: 0 !important; + } + .mb-md-0, + .my-md-0 { + margin-bottom: 0 !important; + } + .ml-md-0, + .mx-md-0 { + margin-left: 0 !important; + } + .m-md-1 { + margin: 0.25rem !important; + } + .mt-md-1, + .my-md-1 { + margin-top: 0.25rem !important; + } + .mr-md-1, + .mx-md-1 { + margin-right: 0.25rem !important; + } + .mb-md-1, + .my-md-1 { + margin-bottom: 0.25rem !important; + } + .ml-md-1, + .mx-md-1 { + margin-left: 0.25rem !important; + } + .m-md-2 { + margin: 0.5rem !important; + } + .mt-md-2, + .my-md-2 { + margin-top: 0.5rem !important; + } + .mr-md-2, + .mx-md-2 { + margin-right: 0.5rem !important; + } + .mb-md-2, + .my-md-2 { + margin-bottom: 0.5rem !important; + } + .ml-md-2, + .mx-md-2 { + margin-left: 0.5rem !important; + } + .m-md-3 { + margin: 1rem !important; + } + .mt-md-3, + .my-md-3 { + margin-top: 1rem !important; + } + .mr-md-3, + .mx-md-3 { + margin-right: 1rem !important; + } + .mb-md-3, + .my-md-3 { + margin-bottom: 1rem !important; + } + .ml-md-3, + .mx-md-3 { + margin-left: 1rem !important; + } + .m-md-4 { + margin: 1.5rem !important; + } + .mt-md-4, + .my-md-4 { + margin-top: 1.5rem !important; + } + .mr-md-4, + .mx-md-4 { + margin-right: 1.5rem !important; + } + .mb-md-4, + .my-md-4 { + margin-bottom: 1.5rem !important; + } + .ml-md-4, + .mx-md-4 { + margin-left: 1.5rem !important; + } + .m-md-5 { + margin: 3rem !important; + } + .mt-md-5, + .my-md-5 { + margin-top: 3rem !important; + } + .mr-md-5, + .mx-md-5 { + margin-right: 3rem !important; + } + .mb-md-5, + .my-md-5 { + margin-bottom: 3rem !important; + } + .ml-md-5, + .mx-md-5 { + margin-left: 3rem !important; + } + .p-md-0 { + padding: 0 !important; + } + .pt-md-0, + .py-md-0 { + padding-top: 0 !important; + } + .pr-md-0, + .px-md-0 { + padding-right: 0 !important; + } + .pb-md-0, + .py-md-0 { + padding-bottom: 0 !important; + } + .pl-md-0, + .px-md-0 { + padding-left: 0 !important; + } + .p-md-1 { + padding: 0.25rem !important; + } + .pt-md-1, + .py-md-1 { + padding-top: 0.25rem !important; + } + .pr-md-1, + .px-md-1 { + padding-right: 0.25rem !important; + } + .pb-md-1, + .py-md-1 { + padding-bottom: 0.25rem !important; + } + .pl-md-1, + .px-md-1 { + padding-left: 0.25rem !important; + } + .p-md-2 { + padding: 0.5rem !important; + } + .pt-md-2, + .py-md-2 { + padding-top: 0.5rem !important; + } + .pr-md-2, + .px-md-2 { + padding-right: 0.5rem !important; + } + .pb-md-2, + .py-md-2 { + padding-bottom: 0.5rem !important; + } + .pl-md-2, + .px-md-2 { + padding-left: 0.5rem !important; + } + .p-md-3 { + padding: 1rem !important; + } + .pt-md-3, + .py-md-3 { + padding-top: 1rem !important; + } + .pr-md-3, + .px-md-3 { + padding-right: 1rem !important; + } + .pb-md-3, + .py-md-3 { + padding-bottom: 1rem !important; + } + .pl-md-3, + .px-md-3 { + padding-left: 1rem !important; + } + .p-md-4 { + padding: 1.5rem !important; + } + .pt-md-4, + .py-md-4 { + padding-top: 1.5rem !important; + } + .pr-md-4, + .px-md-4 { + padding-right: 1.5rem !important; + } + .pb-md-4, + .py-md-4 { + padding-bottom: 1.5rem !important; + } + .pl-md-4, + .px-md-4 { + padding-left: 1.5rem !important; + } + .p-md-5 { + padding: 3rem !important; + } + .pt-md-5, + .py-md-5 { + padding-top: 3rem !important; + } + .pr-md-5, + .px-md-5 { + padding-right: 3rem !important; + } + .pb-md-5, + .py-md-5 { + padding-bottom: 3rem !important; + } + .pl-md-5, + .px-md-5 { + padding-left: 3rem !important; + } + .m-md-n1 { + margin: -0.25rem !important; + } + .mt-md-n1, + .my-md-n1 { + margin-top: -0.25rem !important; + } + .mr-md-n1, + .mx-md-n1 { + margin-right: -0.25rem !important; + } + .mb-md-n1, + .my-md-n1 { + margin-bottom: -0.25rem !important; + } + .ml-md-n1, + .mx-md-n1 { + margin-left: -0.25rem !important; + } + .m-md-n2 { + margin: -0.5rem !important; + } + .mt-md-n2, + .my-md-n2 { + margin-top: -0.5rem !important; + } + .mr-md-n2, + .mx-md-n2 { + margin-right: -0.5rem !important; + } + .mb-md-n2, + .my-md-n2 { + margin-bottom: -0.5rem !important; + } + .ml-md-n2, + .mx-md-n2 { + margin-left: -0.5rem !important; + } + .m-md-n3 { + margin: -1rem !important; + } + .mt-md-n3, + .my-md-n3 { + margin-top: -1rem !important; + } + .mr-md-n3, + .mx-md-n3 { + margin-right: -1rem !important; + } + .mb-md-n3, + .my-md-n3 { + margin-bottom: -1rem !important; + } + .ml-md-n3, + .mx-md-n3 { + margin-left: -1rem !important; + } + .m-md-n4 { + margin: -1.5rem !important; + } + .mt-md-n4, + .my-md-n4 { + margin-top: -1.5rem !important; + } + .mr-md-n4, + .mx-md-n4 { + margin-right: -1.5rem !important; + } + .mb-md-n4, + .my-md-n4 { + margin-bottom: -1.5rem !important; + } + .ml-md-n4, + .mx-md-n4 { + margin-left: -1.5rem !important; + } + .m-md-n5 { + margin: -3rem !important; + } + .mt-md-n5, + .my-md-n5 { + margin-top: -3rem !important; + } + .mr-md-n5, + .mx-md-n5 { + margin-right: -3rem !important; + } + .mb-md-n5, + .my-md-n5 { + margin-bottom: -3rem !important; + } + .ml-md-n5, + .mx-md-n5 { + margin-left: -3rem !important; + } + .m-md-auto { + margin: auto !important; + } + .mt-md-auto, + .my-md-auto { + margin-top: auto !important; + } + .mr-md-auto, + .mx-md-auto { + margin-right: auto !important; + } + .mb-md-auto, + .my-md-auto { + margin-bottom: auto !important; + } + .ml-md-auto, + .mx-md-auto { + margin-left: auto !important; + } +} + +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important; + } + .mt-lg-0, + .my-lg-0 { + margin-top: 0 !important; + } + .mr-lg-0, + .mx-lg-0 { + margin-right: 0 !important; + } + .mb-lg-0, + .my-lg-0 { + margin-bottom: 0 !important; + } + .ml-lg-0, + .mx-lg-0 { + margin-left: 0 !important; + } + .m-lg-1 { + margin: 0.25rem !important; + } + .mt-lg-1, + .my-lg-1 { + margin-top: 0.25rem !important; + } + .mr-lg-1, + .mx-lg-1 { + margin-right: 0.25rem !important; + } + .mb-lg-1, + .my-lg-1 { + margin-bottom: 0.25rem !important; + } + .ml-lg-1, + .mx-lg-1 { + margin-left: 0.25rem !important; + } + .m-lg-2 { + margin: 0.5rem !important; + } + .mt-lg-2, + .my-lg-2 { + margin-top: 0.5rem !important; + } + .mr-lg-2, + .mx-lg-2 { + margin-right: 0.5rem !important; + } + .mb-lg-2, + .my-lg-2 { + margin-bottom: 0.5rem !important; + } + .ml-lg-2, + .mx-lg-2 { + margin-left: 0.5rem !important; + } + .m-lg-3 { + margin: 1rem !important; + } + .mt-lg-3, + .my-lg-3 { + margin-top: 1rem !important; + } + .mr-lg-3, + .mx-lg-3 { + margin-right: 1rem !important; + } + .mb-lg-3, + .my-lg-3 { + margin-bottom: 1rem !important; + } + .ml-lg-3, + .mx-lg-3 { + margin-left: 1rem !important; + } + .m-lg-4 { + margin: 1.5rem !important; + } + .mt-lg-4, + .my-lg-4 { + margin-top: 1.5rem !important; + } + .mr-lg-4, + .mx-lg-4 { + margin-right: 1.5rem !important; + } + .mb-lg-4, + .my-lg-4 { + margin-bottom: 1.5rem !important; + } + .ml-lg-4, + .mx-lg-4 { + margin-left: 1.5rem !important; + } + .m-lg-5 { + margin: 3rem !important; + } + .mt-lg-5, + .my-lg-5 { + margin-top: 3rem !important; + } + .mr-lg-5, + .mx-lg-5 { + margin-right: 3rem !important; + } + .mb-lg-5, + .my-lg-5 { + margin-bottom: 3rem !important; + } + .ml-lg-5, + .mx-lg-5 { + margin-left: 3rem !important; + } + .p-lg-0 { + padding: 0 !important; + } + .pt-lg-0, + .py-lg-0 { + padding-top: 0 !important; + } + .pr-lg-0, + .px-lg-0 { + padding-right: 0 !important; + } + .pb-lg-0, + .py-lg-0 { + padding-bottom: 0 !important; + } + .pl-lg-0, + .px-lg-0 { + padding-left: 0 !important; + } + .p-lg-1 { + padding: 0.25rem !important; + } + .pt-lg-1, + .py-lg-1 { + padding-top: 0.25rem !important; + } + .pr-lg-1, + .px-lg-1 { + padding-right: 0.25rem !important; + } + .pb-lg-1, + .py-lg-1 { + padding-bottom: 0.25rem !important; + } + .pl-lg-1, + .px-lg-1 { + padding-left: 0.25rem !important; + } + .p-lg-2 { + padding: 0.5rem !important; + } + .pt-lg-2, + .py-lg-2 { + padding-top: 0.5rem !important; + } + .pr-lg-2, + .px-lg-2 { + padding-right: 0.5rem !important; + } + .pb-lg-2, + .py-lg-2 { + padding-bottom: 0.5rem !important; + } + .pl-lg-2, + .px-lg-2 { + padding-left: 0.5rem !important; + } + .p-lg-3 { + padding: 1rem !important; + } + .pt-lg-3, + .py-lg-3 { + padding-top: 1rem !important; + } + .pr-lg-3, + .px-lg-3 { + padding-right: 1rem !important; + } + .pb-lg-3, + .py-lg-3 { + padding-bottom: 1rem !important; + } + .pl-lg-3, + .px-lg-3 { + padding-left: 1rem !important; + } + .p-lg-4 { + padding: 1.5rem !important; + } + .pt-lg-4, + .py-lg-4 { + padding-top: 1.5rem !important; + } + .pr-lg-4, + .px-lg-4 { + padding-right: 1.5rem !important; + } + .pb-lg-4, + .py-lg-4 { + padding-bottom: 1.5rem !important; + } + .pl-lg-4, + .px-lg-4 { + padding-left: 1.5rem !important; + } + .p-lg-5 { + padding: 3rem !important; + } + .pt-lg-5, + .py-lg-5 { + padding-top: 3rem !important; + } + .pr-lg-5, + .px-lg-5 { + padding-right: 3rem !important; + } + .pb-lg-5, + .py-lg-5 { + padding-bottom: 3rem !important; + } + .pl-lg-5, + .px-lg-5 { + padding-left: 3rem !important; + } + .m-lg-n1 { + margin: -0.25rem !important; + } + .mt-lg-n1, + .my-lg-n1 { + margin-top: -0.25rem !important; + } + .mr-lg-n1, + .mx-lg-n1 { + margin-right: -0.25rem !important; + } + .mb-lg-n1, + .my-lg-n1 { + margin-bottom: -0.25rem !important; + } + .ml-lg-n1, + .mx-lg-n1 { + margin-left: -0.25rem !important; + } + .m-lg-n2 { + margin: -0.5rem !important; + } + .mt-lg-n2, + .my-lg-n2 { + margin-top: -0.5rem !important; + } + .mr-lg-n2, + .mx-lg-n2 { + margin-right: -0.5rem !important; + } + .mb-lg-n2, + .my-lg-n2 { + margin-bottom: -0.5rem !important; + } + .ml-lg-n2, + .mx-lg-n2 { + margin-left: -0.5rem !important; + } + .m-lg-n3 { + margin: -1rem !important; + } + .mt-lg-n3, + .my-lg-n3 { + margin-top: -1rem !important; + } + .mr-lg-n3, + .mx-lg-n3 { + margin-right: -1rem !important; + } + .mb-lg-n3, + .my-lg-n3 { + margin-bottom: -1rem !important; + } + .ml-lg-n3, + .mx-lg-n3 { + margin-left: -1rem !important; + } + .m-lg-n4 { + margin: -1.5rem !important; + } + .mt-lg-n4, + .my-lg-n4 { + margin-top: -1.5rem !important; + } + .mr-lg-n4, + .mx-lg-n4 { + margin-right: -1.5rem !important; + } + .mb-lg-n4, + .my-lg-n4 { + margin-bottom: -1.5rem !important; + } + .ml-lg-n4, + .mx-lg-n4 { + margin-left: -1.5rem !important; + } + .m-lg-n5 { + margin: -3rem !important; + } + .mt-lg-n5, + .my-lg-n5 { + margin-top: -3rem !important; + } + .mr-lg-n5, + .mx-lg-n5 { + margin-right: -3rem !important; + } + .mb-lg-n5, + .my-lg-n5 { + margin-bottom: -3rem !important; + } + .ml-lg-n5, + .mx-lg-n5 { + margin-left: -3rem !important; + } + .m-lg-auto { + margin: auto !important; + } + .mt-lg-auto, + .my-lg-auto { + margin-top: auto !important; + } + .mr-lg-auto, + .mx-lg-auto { + margin-right: auto !important; + } + .mb-lg-auto, + .my-lg-auto { + margin-bottom: auto !important; + } + .ml-lg-auto, + .mx-lg-auto { + margin-left: auto !important; + } +} + +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important; + } + .mt-xl-0, + .my-xl-0 { + margin-top: 0 !important; + } + .mr-xl-0, + .mx-xl-0 { + margin-right: 0 !important; + } + .mb-xl-0, + .my-xl-0 { + margin-bottom: 0 !important; + } + .ml-xl-0, + .mx-xl-0 { + margin-left: 0 !important; + } + .m-xl-1 { + margin: 0.25rem !important; + } + .mt-xl-1, + .my-xl-1 { + margin-top: 0.25rem !important; + } + .mr-xl-1, + .mx-xl-1 { + margin-right: 0.25rem !important; + } + .mb-xl-1, + .my-xl-1 { + margin-bottom: 0.25rem !important; + } + .ml-xl-1, + .mx-xl-1 { + margin-left: 0.25rem !important; + } + .m-xl-2 { + margin: 0.5rem !important; + } + .mt-xl-2, + .my-xl-2 { + margin-top: 0.5rem !important; + } + .mr-xl-2, + .mx-xl-2 { + margin-right: 0.5rem !important; + } + .mb-xl-2, + .my-xl-2 { + margin-bottom: 0.5rem !important; + } + .ml-xl-2, + .mx-xl-2 { + margin-left: 0.5rem !important; + } + .m-xl-3 { + margin: 1rem !important; + } + .mt-xl-3, + .my-xl-3 { + margin-top: 1rem !important; + } + .mr-xl-3, + .mx-xl-3 { + margin-right: 1rem !important; + } + .mb-xl-3, + .my-xl-3 { + margin-bottom: 1rem !important; + } + .ml-xl-3, + .mx-xl-3 { + margin-left: 1rem !important; + } + .m-xl-4 { + margin: 1.5rem !important; + } + .mt-xl-4, + .my-xl-4 { + margin-top: 1.5rem !important; + } + .mr-xl-4, + .mx-xl-4 { + margin-right: 1.5rem !important; + } + .mb-xl-4, + .my-xl-4 { + margin-bottom: 1.5rem !important; + } + .ml-xl-4, + .mx-xl-4 { + margin-left: 1.5rem !important; + } + .m-xl-5 { + margin: 3rem !important; + } + .mt-xl-5, + .my-xl-5 { + margin-top: 3rem !important; + } + .mr-xl-5, + .mx-xl-5 { + margin-right: 3rem !important; + } + .mb-xl-5, + .my-xl-5 { + margin-bottom: 3rem !important; + } + .ml-xl-5, + .mx-xl-5 { + margin-left: 3rem !important; + } + .p-xl-0 { + padding: 0 !important; + } + .pt-xl-0, + .py-xl-0 { + padding-top: 0 !important; + } + .pr-xl-0, + .px-xl-0 { + padding-right: 0 !important; + } + .pb-xl-0, + .py-xl-0 { + padding-bottom: 0 !important; + } + .pl-xl-0, + .px-xl-0 { + padding-left: 0 !important; + } + .p-xl-1 { + padding: 0.25rem !important; + } + .pt-xl-1, + .py-xl-1 { + padding-top: 0.25rem !important; + } + .pr-xl-1, + .px-xl-1 { + padding-right: 0.25rem !important; + } + .pb-xl-1, + .py-xl-1 { + padding-bottom: 0.25rem !important; + } + .pl-xl-1, + .px-xl-1 { + padding-left: 0.25rem !important; + } + .p-xl-2 { + padding: 0.5rem !important; + } + .pt-xl-2, + .py-xl-2 { + padding-top: 0.5rem !important; + } + .pr-xl-2, + .px-xl-2 { + padding-right: 0.5rem !important; + } + .pb-xl-2, + .py-xl-2 { + padding-bottom: 0.5rem !important; + } + .pl-xl-2, + .px-xl-2 { + padding-left: 0.5rem !important; + } + .p-xl-3 { + padding: 1rem !important; + } + .pt-xl-3, + .py-xl-3 { + padding-top: 1rem !important; + } + .pr-xl-3, + .px-xl-3 { + padding-right: 1rem !important; + } + .pb-xl-3, + .py-xl-3 { + padding-bottom: 1rem !important; + } + .pl-xl-3, + .px-xl-3 { + padding-left: 1rem !important; + } + .p-xl-4 { + padding: 1.5rem !important; + } + .pt-xl-4, + .py-xl-4 { + padding-top: 1.5rem !important; + } + .pr-xl-4, + .px-xl-4 { + padding-right: 1.5rem !important; + } + .pb-xl-4, + .py-xl-4 { + padding-bottom: 1.5rem !important; + } + .pl-xl-4, + .px-xl-4 { + padding-left: 1.5rem !important; + } + .p-xl-5 { + padding: 3rem !important; + } + .pt-xl-5, + .py-xl-5 { + padding-top: 3rem !important; + } + .pr-xl-5, + .px-xl-5 { + padding-right: 3rem !important; + } + .pb-xl-5, + .py-xl-5 { + padding-bottom: 3rem !important; + } + .pl-xl-5, + .px-xl-5 { + padding-left: 3rem !important; + } + .m-xl-n1 { + margin: -0.25rem !important; + } + .mt-xl-n1, + .my-xl-n1 { + margin-top: -0.25rem !important; + } + .mr-xl-n1, + .mx-xl-n1 { + margin-right: -0.25rem !important; + } + .mb-xl-n1, + .my-xl-n1 { + margin-bottom: -0.25rem !important; + } + .ml-xl-n1, + .mx-xl-n1 { + margin-left: -0.25rem !important; + } + .m-xl-n2 { + margin: -0.5rem !important; + } + .mt-xl-n2, + .my-xl-n2 { + margin-top: -0.5rem !important; + } + .mr-xl-n2, + .mx-xl-n2 { + margin-right: -0.5rem !important; + } + .mb-xl-n2, + .my-xl-n2 { + margin-bottom: -0.5rem !important; + } + .ml-xl-n2, + .mx-xl-n2 { + margin-left: -0.5rem !important; + } + .m-xl-n3 { + margin: -1rem !important; + } + .mt-xl-n3, + .my-xl-n3 { + margin-top: -1rem !important; + } + .mr-xl-n3, + .mx-xl-n3 { + margin-right: -1rem !important; + } + .mb-xl-n3, + .my-xl-n3 { + margin-bottom: -1rem !important; + } + .ml-xl-n3, + .mx-xl-n3 { + margin-left: -1rem !important; + } + .m-xl-n4 { + margin: -1.5rem !important; + } + .mt-xl-n4, + .my-xl-n4 { + margin-top: -1.5rem !important; + } + .mr-xl-n4, + .mx-xl-n4 { + margin-right: -1.5rem !important; + } + .mb-xl-n4, + .my-xl-n4 { + margin-bottom: -1.5rem !important; + } + .ml-xl-n4, + .mx-xl-n4 { + margin-left: -1.5rem !important; + } + .m-xl-n5 { + margin: -3rem !important; + } + .mt-xl-n5, + .my-xl-n5 { + margin-top: -3rem !important; + } + .mr-xl-n5, + .mx-xl-n5 { + margin-right: -3rem !important; + } + .mb-xl-n5, + .my-xl-n5 { + margin-bottom: -3rem !important; + } + .ml-xl-n5, + .mx-xl-n5 { + margin-left: -3rem !important; + } + .m-xl-auto { + margin: auto !important; + } + .mt-xl-auto, + .my-xl-auto { + margin-top: auto !important; + } + .mr-xl-auto, + .mx-xl-auto { + margin-right: auto !important; + } + .mb-xl-auto, + .my-xl-auto { + margin-bottom: auto !important; + } + .ml-xl-auto, + .mx-xl-auto { + margin-left: auto !important; + } +} + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; +} + +.text-justify { + text-align: justify !important; +} + +.text-wrap { + white-space: normal !important; +} + +.text-nowrap { + white-space: nowrap !important; +} + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important; + } + .text-sm-right { + text-align: right !important; + } + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .text-md-left { + text-align: left !important; + } + .text-md-right { + text-align: right !important; + } + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important; + } + .text-lg-right { + text-align: right !important; + } + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important; + } + .text-xl-right { + text-align: right !important; + } + .text-xl-center { + text-align: center !important; + } +} + +.text-lowercase { + text-transform: lowercase !important; +} + +.text-uppercase { + text-transform: uppercase !important; +} + +.text-capitalize { + text-transform: capitalize !important; +} + +.font-weight-light { + font-weight: 300 !important; +} + +.font-weight-lighter { + font-weight: lighter !important; +} + +.font-weight-normal { + font-weight: 400 !important; +} + +.font-weight-bold { + font-weight: 700 !important; +} + +.font-weight-bolder { + font-weight: bolder !important; +} + +.font-italic { + font-style: italic !important; +} + +.text-white { + color: #fff !important; +} + +.text-primary { + color: #007bff !important; +} + +a.text-primary:hover, a.text-primary:focus { + color: #0056b3 !important; +} + +.text-secondary { + color: #6c757d !important; +} + +a.text-secondary:hover, a.text-secondary:focus { + color: #494f54 !important; +} + +.text-success { + color: #28a745 !important; +} + +a.text-success:hover, a.text-success:focus { + color: #19692c !important; +} + +.text-info { + color: #17a2b8 !important; +} + +a.text-info:hover, a.text-info:focus { + color: #0f6674 !important; +} + +.text-warning { + color: #ffc107 !important; +} + +a.text-warning:hover, a.text-warning:focus { + color: #ba8b00 !important; +} + +.text-danger { + color: #dc3545 !important; +} + +a.text-danger:hover, a.text-danger:focus { + color: #a71d2a !important; +} + +.text-light { + color: #f8f9fa !important; +} + +a.text-light:hover, a.text-light:focus { + color: #cbd3da !important; +} + +.text-dark { + color: #343a40 !important; +} + +a.text-dark:hover, a.text-dark:focus { + color: #121416 !important; +} + +.text-body { + color: #212529 !important; +} + +.text-muted { + color: #6c757d !important; +} + +.text-black-50 { + color: rgba(0, 0, 0, 0.5) !important; +} + +.text-white-50 { + color: rgba(255, 255, 255, 0.5) !important; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.text-decoration-none { + text-decoration: none !important; +} + +.text-break { + word-break: break-word !important; + overflow-wrap: break-word !important; +} + +.text-reset { + color: inherit !important; +} + +.visible { + visibility: visible !important; +} + +.invisible { + visibility: hidden !important; +} + +@media print { + *, + *::before, + *::after { + text-shadow: none !important; + box-shadow: none !important; + } + a:not(.btn) { + text-decoration: underline; + } + abbr[title]::after { + content: " (" attr(title) ")"; + } + pre { + white-space: pre-wrap !important; + } + pre, + blockquote { + border: 1px solid #adb5bd; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + @page { + size: a3; + } + body { + min-width: 992px !important; + } + .container { + min-width: 992px !important; + } + .navbar { + display: none; + } + .badge { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #dee2e6 !important; + } + .table-dark { + color: inherit; + } + .table-dark th, + .table-dark td, + .table-dark thead th, + .table-dark tbody + tbody { + border-color: #dee2e6; + } + .table .thead-dark th { + color: inherit; + border-color: #dee2e6; + } +} +/*# sourceMappingURL=bootstrap.css.map */ \ No newline at end of file diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/bootstrap/js/bootstrap.bundle.js b/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/bootstrap/js/bootstrap.bundle.js new file mode 100644 index 0000000000..f4f23ead2c --- /dev/null +++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/wwwroot/libs/bootstrap/js/bootstrap.bundle.js @@ -0,0 +1,7013 @@ +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jquery')) : + typeof define === 'function' && define.amd ? define(['exports', 'jquery'], factory) : + (global = global || self, factory(global.bootstrap = {}, global.jQuery)); +}(this, function (exports, $) { 'use strict'; + + $ = $ && $.hasOwnProperty('default') ? $['default'] : $; + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + var ownKeys = Object.keys(source); + + if (typeof Object.getOwnPropertySymbols === 'function') { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + + ownKeys.forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } + + return target; + } + + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + subClass.__proto__ = superClass; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap (v4.3.1): util.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + /** + * ------------------------------------------------------------------------ + * Private TransitionEnd Helpers + * ------------------------------------------------------------------------ + */ + + var TRANSITION_END = 'transitionend'; + var MAX_UID = 1000000; + var MILLISECONDS_MULTIPLIER = 1000; // Shoutout AngusCroll (https://goo.gl/pxwQGp) + + function toType(obj) { + return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase(); + } + + function getSpecialTransitionEndEvent() { + return { + bindType: TRANSITION_END, + delegateType: TRANSITION_END, + handle: function handle(event) { + if ($(event.target).is(this)) { + return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params + } + + return undefined; // eslint-disable-line no-undefined + } + }; + } + + function transitionEndEmulator(duration) { + var _this = this; + + var called = false; + $(this).one(Util.TRANSITION_END, function () { + called = true; + }); + setTimeout(function () { + if (!called) { + Util.triggerTransitionEnd(_this); + } + }, duration); + return this; + } + + function setTransitionEndSupport() { + $.fn.emulateTransitionEnd = transitionEndEmulator; + $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent(); + } + /** + * -------------------------------------------------------------------------- + * Public Util Api + * -------------------------------------------------------------------------- + */ + + + var Util = { + TRANSITION_END: 'bsTransitionEnd', + getUID: function getUID(prefix) { + do { + // eslint-disable-next-line no-bitwise + prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here + } while (document.getElementById(prefix)); + + return prefix; + }, + getSelectorFromElement: function getSelectorFromElement(element) { + var selector = element.getAttribute('data-target'); + + if (!selector || selector === '#') { + var hrefAttr = element.getAttribute('href'); + selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''; + } + + try { + return document.querySelector(selector) ? selector : null; + } catch (err) { + return null; + } + }, + getTransitionDurationFromElement: function getTransitionDurationFromElement(element) { + if (!element) { + return 0; + } // Get transition-duration of the element + + + var transitionDuration = $(element).css('transition-duration'); + var transitionDelay = $(element).css('transition-delay'); + var floatTransitionDuration = parseFloat(transitionDuration); + var floatTransitionDelay = parseFloat(transitionDelay); // Return 0 if element or transition duration is not found + + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0; + } // If multiple durations are defined, take the first + + + transitionDuration = transitionDuration.split(',')[0]; + transitionDelay = transitionDelay.split(',')[0]; + return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; + }, + reflow: function reflow(element) { + return element.offsetHeight; + }, + triggerTransitionEnd: function triggerTransitionEnd(element) { + $(element).trigger(TRANSITION_END); + }, + // TODO: Remove in v5 + supportsTransitionEnd: function supportsTransitionEnd() { + return Boolean(TRANSITION_END); + }, + isElement: function isElement(obj) { + return (obj[0] || obj).nodeType; + }, + typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) { + for (var property in configTypes) { + if (Object.prototype.hasOwnProperty.call(configTypes, property)) { + var expectedTypes = configTypes[property]; + var value = config[property]; + var valueType = value && Util.isElement(value) ? 'element' : toType(value); + + if (!new RegExp(expectedTypes).test(valueType)) { + throw new Error(componentName.toUpperCase() + ": " + ("Option \"" + property + "\" provided type \"" + valueType + "\" ") + ("but expected type \"" + expectedTypes + "\".")); + } + } + } + }, + findShadowRoot: function findShadowRoot(element) { + if (!document.documentElement.attachShadow) { + return null; + } // Can find the shadow root otherwise it'll return the document + + + if (typeof element.getRootNode === 'function') { + var root = element.getRootNode(); + return root instanceof ShadowRoot ? root : null; + } + + if (element instanceof ShadowRoot) { + return element; + } // when we don't find a shadow root + + + if (!element.parentNode) { + return null; + } + + return Util.findShadowRoot(element.parentNode); + } + }; + setTransitionEndSupport(); + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME = 'alert'; + var VERSION = '4.3.1'; + var DATA_KEY = 'bs.alert'; + var EVENT_KEY = "." + DATA_KEY; + var DATA_API_KEY = '.data-api'; + var JQUERY_NO_CONFLICT = $.fn[NAME]; + var Selector = { + DISMISS: '[data-dismiss="alert"]' + }; + var Event = { + CLOSE: "close" + EVENT_KEY, + CLOSED: "closed" + EVENT_KEY, + CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY + }; + var ClassName = { + ALERT: 'alert', + FADE: 'fade', + SHOW: 'show' + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Alert = + /*#__PURE__*/ + function () { + function Alert(element) { + this._element = element; + } // Getters + + + var _proto = Alert.prototype; + + // Public + _proto.close = function close(element) { + var rootElement = this._element; + + if (element) { + rootElement = this._getRootElement(element); + } + + var customEvent = this._triggerCloseEvent(rootElement); + + if (customEvent.isDefaultPrevented()) { + return; + } + + this._removeElement(rootElement); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY); + this._element = null; + } // Private + ; + + _proto._getRootElement = function _getRootElement(element) { + var selector = Util.getSelectorFromElement(element); + var parent = false; + + if (selector) { + parent = document.querySelector(selector); + } + + if (!parent) { + parent = $(element).closest("." + ClassName.ALERT)[0]; + } + + return parent; + }; + + _proto._triggerCloseEvent = function _triggerCloseEvent(element) { + var closeEvent = $.Event(Event.CLOSE); + $(element).trigger(closeEvent); + return closeEvent; + }; + + _proto._removeElement = function _removeElement(element) { + var _this = this; + + $(element).removeClass(ClassName.SHOW); + + if (!$(element).hasClass(ClassName.FADE)) { + this._destroyElement(element); + + return; + } + + var transitionDuration = Util.getTransitionDurationFromElement(element); + $(element).one(Util.TRANSITION_END, function (event) { + return _this._destroyElement(element, event); + }).emulateTransitionEnd(transitionDuration); + }; + + _proto._destroyElement = function _destroyElement(element) { + $(element).detach().trigger(Event.CLOSED).remove(); + } // Static + ; + + Alert._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var $element = $(this); + var data = $element.data(DATA_KEY); + + if (!data) { + data = new Alert(this); + $element.data(DATA_KEY, data); + } + + if (config === 'close') { + data[config](this); + } + }); + }; + + Alert._handleDismiss = function _handleDismiss(alertInstance) { + return function (event) { + if (event) { + event.preventDefault(); + } + + alertInstance.close(this); + }; + }; + + _createClass(Alert, null, [{ + key: "VERSION", + get: function get() { + return VERSION; + } + }]); + + return Alert; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert())); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME] = Alert._jQueryInterface; + $.fn[NAME].Constructor = Alert; + + $.fn[NAME].noConflict = function () { + $.fn[NAME] = JQUERY_NO_CONFLICT; + return Alert._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$1 = 'button'; + var VERSION$1 = '4.3.1'; + var DATA_KEY$1 = 'bs.button'; + var EVENT_KEY$1 = "." + DATA_KEY$1; + var DATA_API_KEY$1 = '.data-api'; + var JQUERY_NO_CONFLICT$1 = $.fn[NAME$1]; + var ClassName$1 = { + ACTIVE: 'active', + BUTTON: 'btn', + FOCUS: 'focus' + }; + var Selector$1 = { + DATA_TOGGLE_CARROT: '[data-toggle^="button"]', + DATA_TOGGLE: '[data-toggle="buttons"]', + INPUT: 'input:not([type="hidden"])', + ACTIVE: '.active', + BUTTON: '.btn' + }; + var Event$1 = { + CLICK_DATA_API: "click" + EVENT_KEY$1 + DATA_API_KEY$1, + FOCUS_BLUR_DATA_API: "focus" + EVENT_KEY$1 + DATA_API_KEY$1 + " " + ("blur" + EVENT_KEY$1 + DATA_API_KEY$1) + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Button = + /*#__PURE__*/ + function () { + function Button(element) { + this._element = element; + } // Getters + + + var _proto = Button.prototype; + + // Public + _proto.toggle = function toggle() { + var triggerChangeEvent = true; + var addAriaPressed = true; + var rootElement = $(this._element).closest(Selector$1.DATA_TOGGLE)[0]; + + if (rootElement) { + var input = this._element.querySelector(Selector$1.INPUT); + + if (input) { + if (input.type === 'radio') { + if (input.checked && this._element.classList.contains(ClassName$1.ACTIVE)) { + triggerChangeEvent = false; + } else { + var activeElement = rootElement.querySelector(Selector$1.ACTIVE); + + if (activeElement) { + $(activeElement).removeClass(ClassName$1.ACTIVE); + } + } + } + + if (triggerChangeEvent) { + if (input.hasAttribute('disabled') || rootElement.hasAttribute('disabled') || input.classList.contains('disabled') || rootElement.classList.contains('disabled')) { + return; + } + + input.checked = !this._element.classList.contains(ClassName$1.ACTIVE); + $(input).trigger('change'); + } + + input.focus(); + addAriaPressed = false; + } + } + + if (addAriaPressed) { + this._element.setAttribute('aria-pressed', !this._element.classList.contains(ClassName$1.ACTIVE)); + } + + if (triggerChangeEvent) { + $(this._element).toggleClass(ClassName$1.ACTIVE); + } + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$1); + this._element = null; + } // Static + ; + + Button._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$1); + + if (!data) { + data = new Button(this); + $(this).data(DATA_KEY$1, data); + } + + if (config === 'toggle') { + data[config](); + } + }); + }; + + _createClass(Button, null, [{ + key: "VERSION", + get: function get() { + return VERSION$1; + } + }]); + + return Button; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(Event$1.CLICK_DATA_API, Selector$1.DATA_TOGGLE_CARROT, function (event) { + event.preventDefault(); + var button = event.target; + + if (!$(button).hasClass(ClassName$1.BUTTON)) { + button = $(button).closest(Selector$1.BUTTON); + } + + Button._jQueryInterface.call($(button), 'toggle'); + }).on(Event$1.FOCUS_BLUR_DATA_API, Selector$1.DATA_TOGGLE_CARROT, function (event) { + var button = $(event.target).closest(Selector$1.BUTTON)[0]; + $(button).toggleClass(ClassName$1.FOCUS, /^focus(in)?$/.test(event.type)); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$1] = Button._jQueryInterface; + $.fn[NAME$1].Constructor = Button; + + $.fn[NAME$1].noConflict = function () { + $.fn[NAME$1] = JQUERY_NO_CONFLICT$1; + return Button._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$2 = 'carousel'; + var VERSION$2 = '4.3.1'; + var DATA_KEY$2 = 'bs.carousel'; + var EVENT_KEY$2 = "." + DATA_KEY$2; + var DATA_API_KEY$2 = '.data-api'; + var JQUERY_NO_CONFLICT$2 = $.fn[NAME$2]; + var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key + + var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key + + var TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch + + var SWIPE_THRESHOLD = 40; + var Default = { + interval: 5000, + keyboard: true, + slide: false, + pause: 'hover', + wrap: true, + touch: true + }; + var DefaultType = { + interval: '(number|boolean)', + keyboard: 'boolean', + slide: '(boolean|string)', + pause: '(string|boolean)', + wrap: 'boolean', + touch: 'boolean' + }; + var Direction = { + NEXT: 'next', + PREV: 'prev', + LEFT: 'left', + RIGHT: 'right' + }; + var Event$2 = { + SLIDE: "slide" + EVENT_KEY$2, + SLID: "slid" + EVENT_KEY$2, + KEYDOWN: "keydown" + EVENT_KEY$2, + MOUSEENTER: "mouseenter" + EVENT_KEY$2, + MOUSELEAVE: "mouseleave" + EVENT_KEY$2, + TOUCHSTART: "touchstart" + EVENT_KEY$2, + TOUCHMOVE: "touchmove" + EVENT_KEY$2, + TOUCHEND: "touchend" + EVENT_KEY$2, + POINTERDOWN: "pointerdown" + EVENT_KEY$2, + POINTERUP: "pointerup" + EVENT_KEY$2, + DRAG_START: "dragstart" + EVENT_KEY$2, + LOAD_DATA_API: "load" + EVENT_KEY$2 + DATA_API_KEY$2, + CLICK_DATA_API: "click" + EVENT_KEY$2 + DATA_API_KEY$2 + }; + var ClassName$2 = { + CAROUSEL: 'carousel', + ACTIVE: 'active', + SLIDE: 'slide', + RIGHT: 'carousel-item-right', + LEFT: 'carousel-item-left', + NEXT: 'carousel-item-next', + PREV: 'carousel-item-prev', + ITEM: 'carousel-item', + POINTER_EVENT: 'pointer-event' + }; + var Selector$2 = { + ACTIVE: '.active', + ACTIVE_ITEM: '.active.carousel-item', + ITEM: '.carousel-item', + ITEM_IMG: '.carousel-item img', + NEXT_PREV: '.carousel-item-next, .carousel-item-prev', + INDICATORS: '.carousel-indicators', + DATA_SLIDE: '[data-slide], [data-slide-to]', + DATA_RIDE: '[data-ride="carousel"]' + }; + var PointerType = { + TOUCH: 'touch', + PEN: 'pen' + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Carousel = + /*#__PURE__*/ + function () { + function Carousel(element, config) { + this._items = null; + this._interval = null; + this._activeElement = null; + this._isPaused = false; + this._isSliding = false; + this.touchTimeout = null; + this.touchStartX = 0; + this.touchDeltaX = 0; + this._config = this._getConfig(config); + this._element = element; + this._indicatorsElement = this._element.querySelector(Selector$2.INDICATORS); + this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; + this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent); + + this._addEventListeners(); + } // Getters + + + var _proto = Carousel.prototype; + + // Public + _proto.next = function next() { + if (!this._isSliding) { + this._slide(Direction.NEXT); + } + }; + + _proto.nextWhenVisible = function nextWhenVisible() { + // Don't call next when the page isn't visible + // or the carousel or its parent isn't visible + if (!document.hidden && $(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden') { + this.next(); + } + }; + + _proto.prev = function prev() { + if (!this._isSliding) { + this._slide(Direction.PREV); + } + }; + + _proto.pause = function pause(event) { + if (!event) { + this._isPaused = true; + } + + if (this._element.querySelector(Selector$2.NEXT_PREV)) { + Util.triggerTransitionEnd(this._element); + this.cycle(true); + } + + clearInterval(this._interval); + this._interval = null; + }; + + _proto.cycle = function cycle(event) { + if (!event) { + this._isPaused = false; + } + + if (this._interval) { + clearInterval(this._interval); + this._interval = null; + } + + if (this._config.interval && !this._isPaused) { + this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval); + } + }; + + _proto.to = function to(index) { + var _this = this; + + this._activeElement = this._element.querySelector(Selector$2.ACTIVE_ITEM); + + var activeIndex = this._getItemIndex(this._activeElement); + + if (index > this._items.length - 1 || index < 0) { + return; + } + + if (this._isSliding) { + $(this._element).one(Event$2.SLID, function () { + return _this.to(index); + }); + return; + } + + if (activeIndex === index) { + this.pause(); + this.cycle(); + return; + } + + var direction = index > activeIndex ? Direction.NEXT : Direction.PREV; + + this._slide(direction, this._items[index]); + }; + + _proto.dispose = function dispose() { + $(this._element).off(EVENT_KEY$2); + $.removeData(this._element, DATA_KEY$2); + this._items = null; + this._config = null; + this._element = null; + this._interval = null; + this._isPaused = null; + this._isSliding = null; + this._activeElement = null; + this._indicatorsElement = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread({}, Default, config); + Util.typeCheckConfig(NAME$2, config, DefaultType); + return config; + }; + + _proto._handleSwipe = function _handleSwipe() { + var absDeltax = Math.abs(this.touchDeltaX); + + if (absDeltax <= SWIPE_THRESHOLD) { + return; + } + + var direction = absDeltax / this.touchDeltaX; // swipe left + + if (direction > 0) { + this.prev(); + } // swipe right + + + if (direction < 0) { + this.next(); + } + }; + + _proto._addEventListeners = function _addEventListeners() { + var _this2 = this; + + if (this._config.keyboard) { + $(this._element).on(Event$2.KEYDOWN, function (event) { + return _this2._keydown(event); + }); + } + + if (this._config.pause === 'hover') { + $(this._element).on(Event$2.MOUSEENTER, function (event) { + return _this2.pause(event); + }).on(Event$2.MOUSELEAVE, function (event) { + return _this2.cycle(event); + }); + } + + if (this._config.touch) { + this._addTouchEventListeners(); + } + }; + + _proto._addTouchEventListeners = function _addTouchEventListeners() { + var _this3 = this; + + if (!this._touchSupported) { + return; + } + + var start = function start(event) { + if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { + _this3.touchStartX = event.originalEvent.clientX; + } else if (!_this3._pointerEvent) { + _this3.touchStartX = event.originalEvent.touches[0].clientX; + } + }; + + var move = function move(event) { + // ensure swiping with one touch and not pinching + if (event.originalEvent.touches && event.originalEvent.touches.length > 1) { + _this3.touchDeltaX = 0; + } else { + _this3.touchDeltaX = event.originalEvent.touches[0].clientX - _this3.touchStartX; + } + }; + + var end = function end(event) { + if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { + _this3.touchDeltaX = event.originalEvent.clientX - _this3.touchStartX; + } + + _this3._handleSwipe(); + + if (_this3._config.pause === 'hover') { + // If it's a touch-enabled device, mouseenter/leave are fired as + // part of the mouse compatibility events on first tap - the carousel + // would stop cycling until user tapped out of it; + // here, we listen for touchend, explicitly pause the carousel + // (as if it's the second time we tap on it, mouseenter compat event + // is NOT fired) and after a timeout (to allow for mouse compatibility + // events to fire) we explicitly restart cycling + _this3.pause(); + + if (_this3.touchTimeout) { + clearTimeout(_this3.touchTimeout); + } + + _this3.touchTimeout = setTimeout(function (event) { + return _this3.cycle(event); + }, TOUCHEVENT_COMPAT_WAIT + _this3._config.interval); + } + }; + + $(this._element.querySelectorAll(Selector$2.ITEM_IMG)).on(Event$2.DRAG_START, function (e) { + return e.preventDefault(); + }); + + if (this._pointerEvent) { + $(this._element).on(Event$2.POINTERDOWN, function (event) { + return start(event); + }); + $(this._element).on(Event$2.POINTERUP, function (event) { + return end(event); + }); + + this._element.classList.add(ClassName$2.POINTER_EVENT); + } else { + $(this._element).on(Event$2.TOUCHSTART, function (event) { + return start(event); + }); + $(this._element).on(Event$2.TOUCHMOVE, function (event) { + return move(event); + }); + $(this._element).on(Event$2.TOUCHEND, function (event) { + return end(event); + }); + } + }; + + _proto._keydown = function _keydown(event) { + if (/input|textarea/i.test(event.target.tagName)) { + return; + } + + switch (event.which) { + case ARROW_LEFT_KEYCODE: + event.preventDefault(); + this.prev(); + break; + + case ARROW_RIGHT_KEYCODE: + event.preventDefault(); + this.next(); + break; + + default: + } + }; + + _proto._getItemIndex = function _getItemIndex(element) { + this._items = element && element.parentNode ? [].slice.call(element.parentNode.querySelectorAll(Selector$2.ITEM)) : []; + return this._items.indexOf(element); + }; + + _proto._getItemByDirection = function _getItemByDirection(direction, activeElement) { + var isNextDirection = direction === Direction.NEXT; + var isPrevDirection = direction === Direction.PREV; + + var activeIndex = this._getItemIndex(activeElement); + + var lastItemIndex = this._items.length - 1; + var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex; + + if (isGoingToWrap && !this._config.wrap) { + return activeElement; + } + + var delta = direction === Direction.PREV ? -1 : 1; + var itemIndex = (activeIndex + delta) % this._items.length; + return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]; + }; + + _proto._triggerSlideEvent = function _triggerSlideEvent(relatedTarget, eventDirectionName) { + var targetIndex = this._getItemIndex(relatedTarget); + + var fromIndex = this._getItemIndex(this._element.querySelector(Selector$2.ACTIVE_ITEM)); + + var slideEvent = $.Event(Event$2.SLIDE, { + relatedTarget: relatedTarget, + direction: eventDirectionName, + from: fromIndex, + to: targetIndex + }); + $(this._element).trigger(slideEvent); + return slideEvent; + }; + + _proto._setActiveIndicatorElement = function _setActiveIndicatorElement(element) { + if (this._indicatorsElement) { + var indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector$2.ACTIVE)); + $(indicators).removeClass(ClassName$2.ACTIVE); + + var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]; + + if (nextIndicator) { + $(nextIndicator).addClass(ClassName$2.ACTIVE); + } + } + }; + + _proto._slide = function _slide(direction, element) { + var _this4 = this; + + var activeElement = this._element.querySelector(Selector$2.ACTIVE_ITEM); + + var activeElementIndex = this._getItemIndex(activeElement); + + var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement); + + var nextElementIndex = this._getItemIndex(nextElement); + + var isCycling = Boolean(this._interval); + var directionalClassName; + var orderClassName; + var eventDirectionName; + + if (direction === Direction.NEXT) { + directionalClassName = ClassName$2.LEFT; + orderClassName = ClassName$2.NEXT; + eventDirectionName = Direction.LEFT; + } else { + directionalClassName = ClassName$2.RIGHT; + orderClassName = ClassName$2.PREV; + eventDirectionName = Direction.RIGHT; + } + + if (nextElement && $(nextElement).hasClass(ClassName$2.ACTIVE)) { + this._isSliding = false; + return; + } + + var slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName); + + if (slideEvent.isDefaultPrevented()) { + return; + } + + if (!activeElement || !nextElement) { + // Some weirdness is happening, so we bail + return; + } + + this._isSliding = true; + + if (isCycling) { + this.pause(); + } + + this._setActiveIndicatorElement(nextElement); + + var slidEvent = $.Event(Event$2.SLID, { + relatedTarget: nextElement, + direction: eventDirectionName, + from: activeElementIndex, + to: nextElementIndex + }); + + if ($(this._element).hasClass(ClassName$2.SLIDE)) { + $(nextElement).addClass(orderClassName); + Util.reflow(nextElement); + $(activeElement).addClass(directionalClassName); + $(nextElement).addClass(directionalClassName); + var nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10); + + if (nextElementInterval) { + this._config.defaultInterval = this._config.defaultInterval || this._config.interval; + this._config.interval = nextElementInterval; + } else { + this._config.interval = this._config.defaultInterval || this._config.interval; + } + + var transitionDuration = Util.getTransitionDurationFromElement(activeElement); + $(activeElement).one(Util.TRANSITION_END, function () { + $(nextElement).removeClass(directionalClassName + " " + orderClassName).addClass(ClassName$2.ACTIVE); + $(activeElement).removeClass(ClassName$2.ACTIVE + " " + orderClassName + " " + directionalClassName); + _this4._isSliding = false; + setTimeout(function () { + return $(_this4._element).trigger(slidEvent); + }, 0); + }).emulateTransitionEnd(transitionDuration); + } else { + $(activeElement).removeClass(ClassName$2.ACTIVE); + $(nextElement).addClass(ClassName$2.ACTIVE); + this._isSliding = false; + $(this._element).trigger(slidEvent); + } + + if (isCycling) { + this.cycle(); + } + } // Static + ; + + Carousel._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$2); + + var _config = _objectSpread({}, Default, $(this).data()); + + if (typeof config === 'object') { + _config = _objectSpread({}, _config, config); + } + + var action = typeof config === 'string' ? config : _config.slide; + + if (!data) { + data = new Carousel(this, _config); + $(this).data(DATA_KEY$2, data); + } + + if (typeof config === 'number') { + data.to(config); + } else if (typeof action === 'string') { + if (typeof data[action] === 'undefined') { + throw new TypeError("No method named \"" + action + "\""); + } + + data[action](); + } else if (_config.interval && _config.ride) { + data.pause(); + data.cycle(); + } + }); + }; + + Carousel._dataApiClickHandler = function _dataApiClickHandler(event) { + var selector = Util.getSelectorFromElement(this); + + if (!selector) { + return; + } + + var target = $(selector)[0]; + + if (!target || !$(target).hasClass(ClassName$2.CAROUSEL)) { + return; + } + + var config = _objectSpread({}, $(target).data(), $(this).data()); + + var slideIndex = this.getAttribute('data-slide-to'); + + if (slideIndex) { + config.interval = false; + } + + Carousel._jQueryInterface.call($(target), config); + + if (slideIndex) { + $(target).data(DATA_KEY$2).to(slideIndex); + } + + event.preventDefault(); + }; + + _createClass(Carousel, null, [{ + key: "VERSION", + get: function get() { + return VERSION$2; + } + }, { + key: "Default", + get: function get() { + return Default; + } + }]); + + return Carousel; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(Event$2.CLICK_DATA_API, Selector$2.DATA_SLIDE, Carousel._dataApiClickHandler); + $(window).on(Event$2.LOAD_DATA_API, function () { + var carousels = [].slice.call(document.querySelectorAll(Selector$2.DATA_RIDE)); + + for (var i = 0, len = carousels.length; i < len; i++) { + var $carousel = $(carousels[i]); + + Carousel._jQueryInterface.call($carousel, $carousel.data()); + } + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$2] = Carousel._jQueryInterface; + $.fn[NAME$2].Constructor = Carousel; + + $.fn[NAME$2].noConflict = function () { + $.fn[NAME$2] = JQUERY_NO_CONFLICT$2; + return Carousel._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$3 = 'collapse'; + var VERSION$3 = '4.3.1'; + var DATA_KEY$3 = 'bs.collapse'; + var EVENT_KEY$3 = "." + DATA_KEY$3; + var DATA_API_KEY$3 = '.data-api'; + var JQUERY_NO_CONFLICT$3 = $.fn[NAME$3]; + var Default$1 = { + toggle: true, + parent: '' + }; + var DefaultType$1 = { + toggle: 'boolean', + parent: '(string|element)' + }; + var Event$3 = { + SHOW: "show" + EVENT_KEY$3, + SHOWN: "shown" + EVENT_KEY$3, + HIDE: "hide" + EVENT_KEY$3, + HIDDEN: "hidden" + EVENT_KEY$3, + CLICK_DATA_API: "click" + EVENT_KEY$3 + DATA_API_KEY$3 + }; + var ClassName$3 = { + SHOW: 'show', + COLLAPSE: 'collapse', + COLLAPSING: 'collapsing', + COLLAPSED: 'collapsed' + }; + var Dimension = { + WIDTH: 'width', + HEIGHT: 'height' + }; + var Selector$3 = { + ACTIVES: '.show, .collapsing', + DATA_TOGGLE: '[data-toggle="collapse"]' + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Collapse = + /*#__PURE__*/ + function () { + function Collapse(element, config) { + this._isTransitioning = false; + this._element = element; + this._config = this._getConfig(config); + this._triggerArray = [].slice.call(document.querySelectorAll("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]"))); + var toggleList = [].slice.call(document.querySelectorAll(Selector$3.DATA_TOGGLE)); + + for (var i = 0, len = toggleList.length; i < len; i++) { + var elem = toggleList[i]; + var selector = Util.getSelectorFromElement(elem); + var filterElement = [].slice.call(document.querySelectorAll(selector)).filter(function (foundElem) { + return foundElem === element; + }); + + if (selector !== null && filterElement.length > 0) { + this._selector = selector; + + this._triggerArray.push(elem); + } + } + + this._parent = this._config.parent ? this._getParent() : null; + + if (!this._config.parent) { + this._addAriaAndCollapsedClass(this._element, this._triggerArray); + } + + if (this._config.toggle) { + this.toggle(); + } + } // Getters + + + var _proto = Collapse.prototype; + + // Public + _proto.toggle = function toggle() { + if ($(this._element).hasClass(ClassName$3.SHOW)) { + this.hide(); + } else { + this.show(); + } + }; + + _proto.show = function show() { + var _this = this; + + if (this._isTransitioning || $(this._element).hasClass(ClassName$3.SHOW)) { + return; + } + + var actives; + var activesData; + + if (this._parent) { + actives = [].slice.call(this._parent.querySelectorAll(Selector$3.ACTIVES)).filter(function (elem) { + if (typeof _this._config.parent === 'string') { + return elem.getAttribute('data-parent') === _this._config.parent; + } + + return elem.classList.contains(ClassName$3.COLLAPSE); + }); + + if (actives.length === 0) { + actives = null; + } + } + + if (actives) { + activesData = $(actives).not(this._selector).data(DATA_KEY$3); + + if (activesData && activesData._isTransitioning) { + return; + } + } + + var startEvent = $.Event(Event$3.SHOW); + $(this._element).trigger(startEvent); + + if (startEvent.isDefaultPrevented()) { + return; + } + + if (actives) { + Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide'); + + if (!activesData) { + $(actives).data(DATA_KEY$3, null); + } + } + + var dimension = this._getDimension(); + + $(this._element).removeClass(ClassName$3.COLLAPSE).addClass(ClassName$3.COLLAPSING); + this._element.style[dimension] = 0; + + if (this._triggerArray.length) { + $(this._triggerArray).removeClass(ClassName$3.COLLAPSED).attr('aria-expanded', true); + } + + this.setTransitioning(true); + + var complete = function complete() { + $(_this._element).removeClass(ClassName$3.COLLAPSING).addClass(ClassName$3.COLLAPSE).addClass(ClassName$3.SHOW); + _this._element.style[dimension] = ''; + + _this.setTransitioning(false); + + $(_this._element).trigger(Event$3.SHOWN); + }; + + var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); + var scrollSize = "scroll" + capitalizedDimension; + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + this._element.style[dimension] = this._element[scrollSize] + "px"; + }; + + _proto.hide = function hide() { + var _this2 = this; + + if (this._isTransitioning || !$(this._element).hasClass(ClassName$3.SHOW)) { + return; + } + + var startEvent = $.Event(Event$3.HIDE); + $(this._element).trigger(startEvent); + + if (startEvent.isDefaultPrevented()) { + return; + } + + var dimension = this._getDimension(); + + this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px"; + Util.reflow(this._element); + $(this._element).addClass(ClassName$3.COLLAPSING).removeClass(ClassName$3.COLLAPSE).removeClass(ClassName$3.SHOW); + var triggerArrayLength = this._triggerArray.length; + + if (triggerArrayLength > 0) { + for (var i = 0; i < triggerArrayLength; i++) { + var trigger = this._triggerArray[i]; + var selector = Util.getSelectorFromElement(trigger); + + if (selector !== null) { + var $elem = $([].slice.call(document.querySelectorAll(selector))); + + if (!$elem.hasClass(ClassName$3.SHOW)) { + $(trigger).addClass(ClassName$3.COLLAPSED).attr('aria-expanded', false); + } + } + } + } + + this.setTransitioning(true); + + var complete = function complete() { + _this2.setTransitioning(false); + + $(_this2._element).removeClass(ClassName$3.COLLAPSING).addClass(ClassName$3.COLLAPSE).trigger(Event$3.HIDDEN); + }; + + this._element.style[dimension] = ''; + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + }; + + _proto.setTransitioning = function setTransitioning(isTransitioning) { + this._isTransitioning = isTransitioning; + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$3); + this._config = null; + this._parent = null; + this._element = null; + this._triggerArray = null; + this._isTransitioning = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread({}, Default$1, config); + config.toggle = Boolean(config.toggle); // Coerce string values + + Util.typeCheckConfig(NAME$3, config, DefaultType$1); + return config; + }; + + _proto._getDimension = function _getDimension() { + var hasWidth = $(this._element).hasClass(Dimension.WIDTH); + return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT; + }; + + _proto._getParent = function _getParent() { + var _this3 = this; + + var parent; + + if (Util.isElement(this._config.parent)) { + parent = this._config.parent; // It's a jQuery object + + if (typeof this._config.parent.jquery !== 'undefined') { + parent = this._config.parent[0]; + } + } else { + parent = document.querySelector(this._config.parent); + } + + var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]"; + var children = [].slice.call(parent.querySelectorAll(selector)); + $(children).each(function (i, element) { + _this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]); + }); + return parent; + }; + + _proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) { + var isOpen = $(element).hasClass(ClassName$3.SHOW); + + if (triggerArray.length) { + $(triggerArray).toggleClass(ClassName$3.COLLAPSED, !isOpen).attr('aria-expanded', isOpen); + } + } // Static + ; + + Collapse._getTargetFromElement = function _getTargetFromElement(element) { + var selector = Util.getSelectorFromElement(element); + return selector ? document.querySelector(selector) : null; + }; + + Collapse._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var $this = $(this); + var data = $this.data(DATA_KEY$3); + + var _config = _objectSpread({}, Default$1, $this.data(), typeof config === 'object' && config ? config : {}); + + if (!data && _config.toggle && /show|hide/.test(config)) { + _config.toggle = false; + } + + if (!data) { + data = new Collapse(this, _config); + $this.data(DATA_KEY$3, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Collapse, null, [{ + key: "VERSION", + get: function get() { + return VERSION$3; + } + }, { + key: "Default", + get: function get() { + return Default$1; + } + }]); + + return Collapse; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(Event$3.CLICK_DATA_API, Selector$3.DATA_TOGGLE, function (event) { + // preventDefault only for elements (which change the URL) not inside the collapsible element + if (event.currentTarget.tagName === 'A') { + event.preventDefault(); + } + + var $trigger = $(this); + var selector = Util.getSelectorFromElement(this); + var selectors = [].slice.call(document.querySelectorAll(selector)); + $(selectors).each(function () { + var $target = $(this); + var data = $target.data(DATA_KEY$3); + var config = data ? 'toggle' : $trigger.data(); + + Collapse._jQueryInterface.call($target, config); + }); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$3] = Collapse._jQueryInterface; + $.fn[NAME$3].Constructor = Collapse; + + $.fn[NAME$3].noConflict = function () { + $.fn[NAME$3] = JQUERY_NO_CONFLICT$3; + return Collapse._jQueryInterface; + }; + + /**! + * @fileOverview Kickass library to create and place poppers near their reference elements. + * @version 1.14.7 + * @license + * Copyright (c) 2016 Federico Zivolo and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'; + + var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox']; + var timeoutDuration = 0; + for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) { + if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) { + timeoutDuration = 1; + break; + } + } + + function microtaskDebounce(fn) { + var called = false; + return function () { + if (called) { + return; + } + called = true; + window.Promise.resolve().then(function () { + called = false; + fn(); + }); + }; + } + + function taskDebounce(fn) { + var scheduled = false; + return function () { + if (!scheduled) { + scheduled = true; + setTimeout(function () { + scheduled = false; + fn(); + }, timeoutDuration); + } + }; + } + + var supportsMicroTasks = isBrowser && window.Promise; + + /** + * Create a debounced version of a method, that's asynchronously deferred + * but called in the minimum time possible. + * + * @method + * @memberof Popper.Utils + * @argument {Function} fn + * @returns {Function} + */ + var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce; + + /** + * Check if the given variable is a function + * @method + * @memberof Popper.Utils + * @argument {Any} functionToCheck - variable to check + * @returns {Boolean} answer to: is a function? + */ + function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; + } + + /** + * Get CSS computed property of the given element + * @method + * @memberof Popper.Utils + * @argument {Eement} element + * @argument {String} property + */ + function getStyleComputedProperty(element, property) { + if (element.nodeType !== 1) { + return []; + } + // NOTE: 1 DOM access here + var window = element.ownerDocument.defaultView; + var css = window.getComputedStyle(element, null); + return property ? css[property] : css; + } + + /** + * Returns the parentNode or the host of the element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} parent + */ + function getParentNode(element) { + if (element.nodeName === 'HTML') { + return element; + } + return element.parentNode || element.host; + } + + /** + * Returns the scrolling parent of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} scroll parent + */ + function getScrollParent(element) { + // Return body, `getScroll` will take care to get the correct `scrollTop` from it + if (!element) { + return document.body; + } + + switch (element.nodeName) { + case 'HTML': + case 'BODY': + return element.ownerDocument.body; + case '#document': + return element.body; + } + + // Firefox want us to check `-x` and `-y` variations as well + + var _getStyleComputedProp = getStyleComputedProperty(element), + overflow = _getStyleComputedProp.overflow, + overflowX = _getStyleComputedProp.overflowX, + overflowY = _getStyleComputedProp.overflowY; + + if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { + return element; + } + + return getScrollParent(getParentNode(element)); + } + + var isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode); + var isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent); + + /** + * Determines if the browser is Internet Explorer + * @method + * @memberof Popper.Utils + * @param {Number} version to check + * @returns {Boolean} isIE + */ + function isIE(version) { + if (version === 11) { + return isIE11; + } + if (version === 10) { + return isIE10; + } + return isIE11 || isIE10; + } + + /** + * Returns the offset parent of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} offset parent + */ + function getOffsetParent(element) { + if (!element) { + return document.documentElement; + } + + var noOffsetParent = isIE(10) ? document.body : null; + + // NOTE: 1 DOM access here + var offsetParent = element.offsetParent || null; + // Skip hidden elements which don't have an offsetParent + while (offsetParent === noOffsetParent && element.nextElementSibling) { + offsetParent = (element = element.nextElementSibling).offsetParent; + } + + var nodeName = offsetParent && offsetParent.nodeName; + + if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') { + return element ? element.ownerDocument.documentElement : document.documentElement; + } + + // .offsetParent will return the closest TH, TD or TABLE in case + // no offsetParent is present, I hate this job... + if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') { + return getOffsetParent(offsetParent); + } + + return offsetParent; + } + + function isOffsetContainer(element) { + var nodeName = element.nodeName; + + if (nodeName === 'BODY') { + return false; + } + return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element; + } + + /** + * Finds the root node (document, shadowDOM root) of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} node + * @returns {Element} root node + */ + function getRoot(node) { + if (node.parentNode !== null) { + return getRoot(node.parentNode); + } + + return node; + } + + /** + * Finds the offset parent common to the two provided nodes + * @method + * @memberof Popper.Utils + * @argument {Element} element1 + * @argument {Element} element2 + * @returns {Element} common offset parent + */ + function findCommonOffsetParent(element1, element2) { + // This check is needed to avoid errors in case one of the elements isn't defined for any reason + if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) { + return document.documentElement; + } + + // Here we make sure to give as "start" the element that comes first in the DOM + var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING; + var start = order ? element1 : element2; + var end = order ? element2 : element1; + + // Get common ancestor container + var range = document.createRange(); + range.setStart(start, 0); + range.setEnd(end, 0); + var commonAncestorContainer = range.commonAncestorContainer; + + // Both nodes are inside #document + + if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) { + if (isOffsetContainer(commonAncestorContainer)) { + return commonAncestorContainer; + } + + return getOffsetParent(commonAncestorContainer); + } + + // one of the nodes is inside shadowDOM, find which one + var element1root = getRoot(element1); + if (element1root.host) { + return findCommonOffsetParent(element1root.host, element2); + } else { + return findCommonOffsetParent(element1, getRoot(element2).host); + } + } + + /** + * Gets the scroll value of the given element in the given side (top and left) + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @argument {String} side `top` or `left` + * @returns {number} amount of scrolled pixels + */ + function getScroll(element) { + var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top'; + + var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft'; + var nodeName = element.nodeName; + + if (nodeName === 'BODY' || nodeName === 'HTML') { + var html = element.ownerDocument.documentElement; + var scrollingElement = element.ownerDocument.scrollingElement || html; + return scrollingElement[upperSide]; + } + + return element[upperSide]; + } + + /* + * Sum or subtract the element scroll values (left and top) from a given rect object + * @method + * @memberof Popper.Utils + * @param {Object} rect - Rect object you want to change + * @param {HTMLElement} element - The element from the function reads the scroll values + * @param {Boolean} subtract - set to true if you want to subtract the scroll values + * @return {Object} rect - The modifier rect object + */ + function includeScroll(rect, element) { + var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var scrollTop = getScroll(element, 'top'); + var scrollLeft = getScroll(element, 'left'); + var modifier = subtract ? -1 : 1; + rect.top += scrollTop * modifier; + rect.bottom += scrollTop * modifier; + rect.left += scrollLeft * modifier; + rect.right += scrollLeft * modifier; + return rect; + } + + /* + * Helper to detect borders of a given element + * @method + * @memberof Popper.Utils + * @param {CSSStyleDeclaration} styles + * Result of `getStyleComputedProperty` on the given element + * @param {String} axis - `x` or `y` + * @return {number} borders - The borders size of the given axis + */ + + function getBordersSize(styles, axis) { + var sideA = axis === 'x' ? 'Left' : 'Top'; + var sideB = sideA === 'Left' ? 'Right' : 'Bottom'; + + return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10); + } + + function getSize(axis, body, html, computedStyle) { + return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? parseInt(html['offset' + axis]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')]) : 0); + } + + function getWindowSizes(document) { + var body = document.body; + var html = document.documentElement; + var computedStyle = isIE(10) && getComputedStyle(html); + + return { + height: getSize('Height', body, html, computedStyle), + width: getSize('Width', body, html, computedStyle) + }; + } + + var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + + var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + + + + + var defineProperty = function (obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + }; + + var _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + /** + * Given element offsets, generate an output similar to getBoundingClientRect + * @method + * @memberof Popper.Utils + * @argument {Object} offsets + * @returns {Object} ClientRect like output + */ + function getClientRect(offsets) { + return _extends({}, offsets, { + right: offsets.left + offsets.width, + bottom: offsets.top + offsets.height + }); + } + + /** + * Get bounding client rect of given element + * @method + * @memberof Popper.Utils + * @param {HTMLElement} element + * @return {Object} client rect + */ + function getBoundingClientRect(element) { + var rect = {}; + + // IE10 10 FIX: Please, don't ask, the element isn't + // considered in DOM in some circumstances... + // This isn't reproducible in IE10 compatibility mode of IE11 + try { + if (isIE(10)) { + rect = element.getBoundingClientRect(); + var scrollTop = getScroll(element, 'top'); + var scrollLeft = getScroll(element, 'left'); + rect.top += scrollTop; + rect.left += scrollLeft; + rect.bottom += scrollTop; + rect.right += scrollLeft; + } else { + rect = element.getBoundingClientRect(); + } + } catch (e) {} + + var result = { + left: rect.left, + top: rect.top, + width: rect.right - rect.left, + height: rect.bottom - rect.top + }; + + // subtract scrollbar size from sizes + var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {}; + var width = sizes.width || element.clientWidth || result.right - result.left; + var height = sizes.height || element.clientHeight || result.bottom - result.top; + + var horizScrollbar = element.offsetWidth - width; + var vertScrollbar = element.offsetHeight - height; + + // if an hypothetical scrollbar is detected, we must be sure it's not a `border` + // we make this check conditional for performance reasons + if (horizScrollbar || vertScrollbar) { + var styles = getStyleComputedProperty(element); + horizScrollbar -= getBordersSize(styles, 'x'); + vertScrollbar -= getBordersSize(styles, 'y'); + + result.width -= horizScrollbar; + result.height -= vertScrollbar; + } + + return getClientRect(result); + } + + function getOffsetRectRelativeToArbitraryNode(children, parent) { + var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var isIE10 = isIE(10); + var isHTML = parent.nodeName === 'HTML'; + var childrenRect = getBoundingClientRect(children); + var parentRect = getBoundingClientRect(parent); + var scrollParent = getScrollParent(children); + + var styles = getStyleComputedProperty(parent); + var borderTopWidth = parseFloat(styles.borderTopWidth, 10); + var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10); + + // In cases where the parent is fixed, we must ignore negative scroll in offset calc + if (fixedPosition && isHTML) { + parentRect.top = Math.max(parentRect.top, 0); + parentRect.left = Math.max(parentRect.left, 0); + } + var offsets = getClientRect({ + top: childrenRect.top - parentRect.top - borderTopWidth, + left: childrenRect.left - parentRect.left - borderLeftWidth, + width: childrenRect.width, + height: childrenRect.height + }); + offsets.marginTop = 0; + offsets.marginLeft = 0; + + // Subtract margins of documentElement in case it's being used as parent + // we do this only on HTML because it's the only element that behaves + // differently when margins are applied to it. The margins are included in + // the box of the documentElement, in the other cases not. + if (!isIE10 && isHTML) { + var marginTop = parseFloat(styles.marginTop, 10); + var marginLeft = parseFloat(styles.marginLeft, 10); + + offsets.top -= borderTopWidth - marginTop; + offsets.bottom -= borderTopWidth - marginTop; + offsets.left -= borderLeftWidth - marginLeft; + offsets.right -= borderLeftWidth - marginLeft; + + // Attach marginTop and marginLeft because in some circumstances we may need them + offsets.marginTop = marginTop; + offsets.marginLeft = marginLeft; + } + + if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') { + offsets = includeScroll(offsets, parent); + } + + return offsets; + } + + function getViewportOffsetRectRelativeToArtbitraryNode(element) { + var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var html = element.ownerDocument.documentElement; + var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html); + var width = Math.max(html.clientWidth, window.innerWidth || 0); + var height = Math.max(html.clientHeight, window.innerHeight || 0); + + var scrollTop = !excludeScroll ? getScroll(html) : 0; + var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0; + + var offset = { + top: scrollTop - relativeOffset.top + relativeOffset.marginTop, + left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft, + width: width, + height: height + }; + + return getClientRect(offset); + } + + /** + * Check if the given element is fixed or is inside a fixed parent + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @argument {Element} customContainer + * @returns {Boolean} answer to "isFixed?" + */ + function isFixed(element) { + var nodeName = element.nodeName; + if (nodeName === 'BODY' || nodeName === 'HTML') { + return false; + } + if (getStyleComputedProperty(element, 'position') === 'fixed') { + return true; + } + var parentNode = getParentNode(element); + if (!parentNode) { + return false; + } + return isFixed(parentNode); + } + + /** + * Finds the first parent of an element that has a transformed property defined + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} first transformed parent or documentElement + */ + + function getFixedPositionOffsetParent(element) { + // This check is needed to avoid errors in case one of the elements isn't defined for any reason + if (!element || !element.parentElement || isIE()) { + return document.documentElement; + } + var el = element.parentElement; + while (el && getStyleComputedProperty(el, 'transform') === 'none') { + el = el.parentElement; + } + return el || document.documentElement; + } + + /** + * Computed the boundaries limits and return them + * @method + * @memberof Popper.Utils + * @param {HTMLElement} popper + * @param {HTMLElement} reference + * @param {number} padding + * @param {HTMLElement} boundariesElement - Element used to define the boundaries + * @param {Boolean} fixedPosition - Is in fixed position mode + * @returns {Object} Coordinates of the boundaries + */ + function getBoundaries(popper, reference, padding, boundariesElement) { + var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + + // NOTE: 1 DOM access here + + var boundaries = { top: 0, left: 0 }; + var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference); + + // Handle viewport case + if (boundariesElement === 'viewport') { + boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition); + } else { + // Handle other cases based on DOM element used as boundaries + var boundariesNode = void 0; + if (boundariesElement === 'scrollParent') { + boundariesNode = getScrollParent(getParentNode(reference)); + if (boundariesNode.nodeName === 'BODY') { + boundariesNode = popper.ownerDocument.documentElement; + } + } else if (boundariesElement === 'window') { + boundariesNode = popper.ownerDocument.documentElement; + } else { + boundariesNode = boundariesElement; + } + + var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition); + + // In case of HTML, we need a different computation + if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) { + var _getWindowSizes = getWindowSizes(popper.ownerDocument), + height = _getWindowSizes.height, + width = _getWindowSizes.width; + + boundaries.top += offsets.top - offsets.marginTop; + boundaries.bottom = height + offsets.top; + boundaries.left += offsets.left - offsets.marginLeft; + boundaries.right = width + offsets.left; + } else { + // for all the other DOM elements, this one is good + boundaries = offsets; + } + } + + // Add paddings + padding = padding || 0; + var isPaddingNumber = typeof padding === 'number'; + boundaries.left += isPaddingNumber ? padding : padding.left || 0; + boundaries.top += isPaddingNumber ? padding : padding.top || 0; + boundaries.right -= isPaddingNumber ? padding : padding.right || 0; + boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0; + + return boundaries; + } + + function getArea(_ref) { + var width = _ref.width, + height = _ref.height; + + return width * height; + } + + /** + * Utility used to transform the `auto` placement to the placement with more + * available space. + * @method + * @memberof Popper.Utils + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) { + var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0; + + if (placement.indexOf('auto') === -1) { + return placement; + } + + var boundaries = getBoundaries(popper, reference, padding, boundariesElement); + + var rects = { + top: { + width: boundaries.width, + height: refRect.top - boundaries.top + }, + right: { + width: boundaries.right - refRect.right, + height: boundaries.height + }, + bottom: { + width: boundaries.width, + height: boundaries.bottom - refRect.bottom + }, + left: { + width: refRect.left - boundaries.left, + height: boundaries.height + } + }; + + var sortedAreas = Object.keys(rects).map(function (key) { + return _extends({ + key: key + }, rects[key], { + area: getArea(rects[key]) + }); + }).sort(function (a, b) { + return b.area - a.area; + }); + + var filteredAreas = sortedAreas.filter(function (_ref2) { + var width = _ref2.width, + height = _ref2.height; + return width >= popper.clientWidth && height >= popper.clientHeight; + }); + + var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key; + + var variation = placement.split('-')[1]; + + return computedPlacement + (variation ? '-' + variation : ''); + } + + /** + * Get offsets to the reference element + * @method + * @memberof Popper.Utils + * @param {Object} state + * @param {Element} popper - the popper element + * @param {Element} reference - the reference element (the popper will be relative to this) + * @param {Element} fixedPosition - is in fixed position mode + * @returns {Object} An object containing the offsets which will be applied to the popper + */ + function getReferenceOffsets(state, popper, reference) { + var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; + + var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference); + return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition); + } + + /** + * Get the outer sizes of the given element (offset size + margins) + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Object} object containing width and height properties + */ + function getOuterSizes(element) { + var window = element.ownerDocument.defaultView; + var styles = window.getComputedStyle(element); + var x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0); + var y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0); + var result = { + width: element.offsetWidth + y, + height: element.offsetHeight + x + }; + return result; + } + + /** + * Get the opposite placement of the given one + * @method + * @memberof Popper.Utils + * @argument {String} placement + * @returns {String} flipped placement + */ + function getOppositePlacement(placement) { + var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' }; + return placement.replace(/left|right|bottom|top/g, function (matched) { + return hash[matched]; + }); + } + + /** + * Get offsets to the popper + * @method + * @memberof Popper.Utils + * @param {Object} position - CSS position the Popper will get applied + * @param {HTMLElement} popper - the popper element + * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this) + * @param {String} placement - one of the valid placement options + * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper + */ + function getPopperOffsets(popper, referenceOffsets, placement) { + placement = placement.split('-')[0]; + + // Get popper node sizes + var popperRect = getOuterSizes(popper); + + // Add position, width and height to our offsets object + var popperOffsets = { + width: popperRect.width, + height: popperRect.height + }; + + // depending by the popper placement we have to compute its offsets slightly differently + var isHoriz = ['right', 'left'].indexOf(placement) !== -1; + var mainSide = isHoriz ? 'top' : 'left'; + var secondarySide = isHoriz ? 'left' : 'top'; + var measurement = isHoriz ? 'height' : 'width'; + var secondaryMeasurement = !isHoriz ? 'height' : 'width'; + + popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2; + if (placement === secondarySide) { + popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement]; + } else { + popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)]; + } + + return popperOffsets; + } + + /** + * Mimics the `find` method of Array + * @method + * @memberof Popper.Utils + * @argument {Array} arr + * @argument prop + * @argument value + * @returns index or -1 + */ + function find(arr, check) { + // use native find if supported + if (Array.prototype.find) { + return arr.find(check); + } + + // use `filter` to obtain the same behavior of `find` + return arr.filter(check)[0]; + } + + /** + * Return the index of the matching object + * @method + * @memberof Popper.Utils + * @argument {Array} arr + * @argument prop + * @argument value + * @returns index or -1 + */ + function findIndex(arr, prop, value) { + // use native findIndex if supported + if (Array.prototype.findIndex) { + return arr.findIndex(function (cur) { + return cur[prop] === value; + }); + } + + // use `find` + `indexOf` if `findIndex` isn't supported + var match = find(arr, function (obj) { + return obj[prop] === value; + }); + return arr.indexOf(match); + } + + /** + * Loop trough the list of modifiers and run them in order, + * each of them will then edit the data object. + * @method + * @memberof Popper.Utils + * @param {dataObject} data + * @param {Array} modifiers + * @param {String} ends - Optional modifier name used as stopper + * @returns {dataObject} + */ + function runModifiers(modifiers, data, ends) { + var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends)); + + modifiersToRun.forEach(function (modifier) { + if (modifier['function']) { + // eslint-disable-line dot-notation + console.warn('`modifier.function` is deprecated, use `modifier.fn`!'); + } + var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation + if (modifier.enabled && isFunction(fn)) { + // Add properties to offsets to make them a complete clientRect object + // we do this before each modifier to make sure the previous one doesn't + // mess with these values + data.offsets.popper = getClientRect(data.offsets.popper); + data.offsets.reference = getClientRect(data.offsets.reference); + + data = fn(data, modifier); + } + }); + + return data; + } + + /** + * Updates the position of the popper, computing the new offsets and applying + * the new style.
+ * Prefer `scheduleUpdate` over `update` because of performance reasons. + * @method + * @memberof Popper + */ + function update() { + // if popper is destroyed, don't perform any further update + if (this.state.isDestroyed) { + return; + } + + var data = { + instance: this, + styles: {}, + arrowStyles: {}, + attributes: {}, + flipped: false, + offsets: {} + }; + + // compute reference element offsets + data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed); + + // compute auto placement, store placement inside the data object, + // modifiers will be able to edit `placement` if needed + // and refer to originalPlacement to know the original value + data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding); + + // store the computed placement inside `originalPlacement` + data.originalPlacement = data.placement; + + data.positionFixed = this.options.positionFixed; + + // compute the popper offsets + data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement); + + data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute'; + + // run the modifiers + data = runModifiers(this.modifiers, data); + + // the first `update` will call `onCreate` callback + // the other ones will call `onUpdate` callback + if (!this.state.isCreated) { + this.state.isCreated = true; + this.options.onCreate(data); + } else { + this.options.onUpdate(data); + } + } + + /** + * Helper used to know if the given modifier is enabled. + * @method + * @memberof Popper.Utils + * @returns {Boolean} + */ + function isModifierEnabled(modifiers, modifierName) { + return modifiers.some(function (_ref) { + var name = _ref.name, + enabled = _ref.enabled; + return enabled && name === modifierName; + }); + } + + /** + * Get the prefixed supported property name + * @method + * @memberof Popper.Utils + * @argument {String} property (camelCase) + * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix) + */ + function getSupportedPropertyName(property) { + var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O']; + var upperProp = property.charAt(0).toUpperCase() + property.slice(1); + + for (var i = 0; i < prefixes.length; i++) { + var prefix = prefixes[i]; + var toCheck = prefix ? '' + prefix + upperProp : property; + if (typeof document.body.style[toCheck] !== 'undefined') { + return toCheck; + } + } + return null; + } + + /** + * Destroys the popper. + * @method + * @memberof Popper + */ + function destroy() { + this.state.isDestroyed = true; + + // touch DOM only if `applyStyle` modifier is enabled + if (isModifierEnabled(this.modifiers, 'applyStyle')) { + this.popper.removeAttribute('x-placement'); + this.popper.style.position = ''; + this.popper.style.top = ''; + this.popper.style.left = ''; + this.popper.style.right = ''; + this.popper.style.bottom = ''; + this.popper.style.willChange = ''; + this.popper.style[getSupportedPropertyName('transform')] = ''; + } + + this.disableEventListeners(); + + // remove the popper if user explicity asked for the deletion on destroy + // do not use `remove` because IE11 doesn't support it + if (this.options.removeOnDestroy) { + this.popper.parentNode.removeChild(this.popper); + } + return this; + } + + /** + * Get the window associated with the element + * @argument {Element} element + * @returns {Window} + */ + function getWindow(element) { + var ownerDocument = element.ownerDocument; + return ownerDocument ? ownerDocument.defaultView : window; + } + + function attachToScrollParents(scrollParent, event, callback, scrollParents) { + var isBody = scrollParent.nodeName === 'BODY'; + var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent; + target.addEventListener(event, callback, { passive: true }); + + if (!isBody) { + attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents); + } + scrollParents.push(target); + } + + /** + * Setup needed event listeners used to update the popper position + * @method + * @memberof Popper.Utils + * @private + */ + function setupEventListeners(reference, options, state, updateBound) { + // Resize event listener on window + state.updateBound = updateBound; + getWindow(reference).addEventListener('resize', state.updateBound, { passive: true }); + + // Scroll event listener on scroll parents + var scrollElement = getScrollParent(reference); + attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents); + state.scrollElement = scrollElement; + state.eventsEnabled = true; + + return state; + } + + /** + * It will add resize/scroll events and start recalculating + * position of the popper element when they are triggered. + * @method + * @memberof Popper + */ + function enableEventListeners() { + if (!this.state.eventsEnabled) { + this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate); + } + } + + /** + * Remove event listeners used to update the popper position + * @method + * @memberof Popper.Utils + * @private + */ + function removeEventListeners(reference, state) { + // Remove resize event listener on window + getWindow(reference).removeEventListener('resize', state.updateBound); + + // Remove scroll event listener on scroll parents + state.scrollParents.forEach(function (target) { + target.removeEventListener('scroll', state.updateBound); + }); + + // Reset state + state.updateBound = null; + state.scrollParents = []; + state.scrollElement = null; + state.eventsEnabled = false; + return state; + } + + /** + * It will remove resize/scroll events and won't recalculate popper position + * when they are triggered. It also won't trigger `onUpdate` callback anymore, + * unless you call `update` method manually. + * @method + * @memberof Popper + */ + function disableEventListeners() { + if (this.state.eventsEnabled) { + cancelAnimationFrame(this.scheduleUpdate); + this.state = removeEventListeners(this.reference, this.state); + } + } + + /** + * Tells if a given input is a number + * @method + * @memberof Popper.Utils + * @param {*} input to check + * @return {Boolean} + */ + function isNumeric(n) { + return n !== '' && !isNaN(parseFloat(n)) && isFinite(n); + } + + /** + * Set the style to the given popper + * @method + * @memberof Popper.Utils + * @argument {Element} element - Element to apply the style to + * @argument {Object} styles + * Object with a list of properties and values which will be applied to the element + */ + function setStyles(element, styles) { + Object.keys(styles).forEach(function (prop) { + var unit = ''; + // add unit if the value is numeric and is one of the following + if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) { + unit = 'px'; + } + element.style[prop] = styles[prop] + unit; + }); + } + + /** + * Set the attributes to the given popper + * @method + * @memberof Popper.Utils + * @argument {Element} element - Element to apply the attributes to + * @argument {Object} styles + * Object with a list of properties and values which will be applied to the element + */ + function setAttributes(element, attributes) { + Object.keys(attributes).forEach(function (prop) { + var value = attributes[prop]; + if (value !== false) { + element.setAttribute(prop, attributes[prop]); + } else { + element.removeAttribute(prop); + } + }); + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} data.styles - List of style properties - values to apply to popper element + * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The same data object + */ + function applyStyle(data) { + // any property present in `data.styles` will be applied to the popper, + // in this way we can make the 3rd party modifiers add custom styles to it + // Be aware, modifiers could override the properties defined in the previous + // lines of this modifier! + setStyles(data.instance.popper, data.styles); + + // any property present in `data.attributes` will be applied to the popper, + // they will be set as HTML attributes of the element + setAttributes(data.instance.popper, data.attributes); + + // if arrowElement is defined and arrowStyles has some properties + if (data.arrowElement && Object.keys(data.arrowStyles).length) { + setStyles(data.arrowElement, data.arrowStyles); + } + + return data; + } + + /** + * Set the x-placement attribute before everything else because it could be used + * to add margins to the popper margins needs to be calculated to get the + * correct popper offsets. + * @method + * @memberof Popper.modifiers + * @param {HTMLElement} reference - The reference element used to position the popper + * @param {HTMLElement} popper - The HTML element used as popper + * @param {Object} options - Popper.js options + */ + function applyStyleOnLoad(reference, popper, options, modifierOptions, state) { + // compute reference element offsets + var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed); + + // compute auto placement, store placement inside the data object, + // modifiers will be able to edit `placement` if needed + // and refer to originalPlacement to know the original value + var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding); + + popper.setAttribute('x-placement', placement); + + // Apply `position` to popper before anything else because + // without the position applied we can't guarantee correct computations + setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' }); + + return options; + } + + /** + * @function + * @memberof Popper.Utils + * @argument {Object} data - The data object generated by `update` method + * @argument {Boolean} shouldRound - If the offsets should be rounded at all + * @returns {Object} The popper's position offsets rounded + * + * The tale of pixel-perfect positioning. It's still not 100% perfect, but as + * good as it can be within reason. + * Discussion here: https://github.com/FezVrasta/popper.js/pull/715 + * + * Low DPI screens cause a popper to be blurry if not using full pixels (Safari + * as well on High DPI screens). + * + * Firefox prefers no rounding for positioning and does not have blurriness on + * high DPI screens. + * + * Only horizontal placement and left/right values need to be considered. + */ + function getRoundedOffsets(data, shouldRound) { + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + var round = Math.round, + floor = Math.floor; + + var noRound = function noRound(v) { + return v; + }; + + var referenceWidth = round(reference.width); + var popperWidth = round(popper.width); + + var isVertical = ['left', 'right'].indexOf(data.placement) !== -1; + var isVariation = data.placement.indexOf('-') !== -1; + var sameWidthParity = referenceWidth % 2 === popperWidth % 2; + var bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1; + + var horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor; + var verticalToInteger = !shouldRound ? noRound : round; + + return { + left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left), + top: verticalToInteger(popper.top), + bottom: verticalToInteger(popper.bottom), + right: horizontalToInteger(popper.right) + }; + } + + var isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent); + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function computeStyle(data, options) { + var x = options.x, + y = options.y; + var popper = data.offsets.popper; + + // Remove this legacy support in Popper.js v2 + + var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) { + return modifier.name === 'applyStyle'; + }).gpuAcceleration; + if (legacyGpuAccelerationOption !== undefined) { + console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!'); + } + var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration; + + var offsetParent = getOffsetParent(data.instance.popper); + var offsetParentRect = getBoundingClientRect(offsetParent); + + // Styles + var styles = { + position: popper.position + }; + + var offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox); + + var sideA = x === 'bottom' ? 'top' : 'bottom'; + var sideB = y === 'right' ? 'left' : 'right'; + + // if gpuAcceleration is set to `true` and transform is supported, + // we use `translate3d` to apply the position to the popper we + // automatically use the supported prefixed version if needed + var prefixedProperty = getSupportedPropertyName('transform'); + + // now, let's make a step back and look at this code closely (wtf?) + // If the content of the popper grows once it's been positioned, it + // may happen that the popper gets misplaced because of the new content + // overflowing its reference element + // To avoid this problem, we provide two options (x and y), which allow + // the consumer to define the offset origin. + // If we position a popper on top of a reference element, we can set + // `x` to `top` to make the popper grow towards its top instead of + // its bottom. + var left = void 0, + top = void 0; + if (sideA === 'bottom') { + // when offsetParent is the positioning is relative to the bottom of the screen (excluding the scrollbar) + // and not the bottom of the html element + if (offsetParent.nodeName === 'HTML') { + top = -offsetParent.clientHeight + offsets.bottom; + } else { + top = -offsetParentRect.height + offsets.bottom; + } + } else { + top = offsets.top; + } + if (sideB === 'right') { + if (offsetParent.nodeName === 'HTML') { + left = -offsetParent.clientWidth + offsets.right; + } else { + left = -offsetParentRect.width + offsets.right; + } + } else { + left = offsets.left; + } + if (gpuAcceleration && prefixedProperty) { + styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; + styles[sideA] = 0; + styles[sideB] = 0; + styles.willChange = 'transform'; + } else { + // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties + var invertTop = sideA === 'bottom' ? -1 : 1; + var invertLeft = sideB === 'right' ? -1 : 1; + styles[sideA] = top * invertTop; + styles[sideB] = left * invertLeft; + styles.willChange = sideA + ', ' + sideB; + } + + // Attributes + var attributes = { + 'x-placement': data.placement + }; + + // Update `data` attributes, styles and arrowStyles + data.attributes = _extends({}, attributes, data.attributes); + data.styles = _extends({}, styles, data.styles); + data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles); + + return data; + } + + /** + * Helper used to know if the given modifier depends from another one.
+ * It checks if the needed modifier is listed and enabled. + * @method + * @memberof Popper.Utils + * @param {Array} modifiers - list of modifiers + * @param {String} requestingName - name of requesting modifier + * @param {String} requestedName - name of requested modifier + * @returns {Boolean} + */ + function isModifierRequired(modifiers, requestingName, requestedName) { + var requesting = find(modifiers, function (_ref) { + var name = _ref.name; + return name === requestingName; + }); + + var isRequired = !!requesting && modifiers.some(function (modifier) { + return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order; + }); + + if (!isRequired) { + var _requesting = '`' + requestingName + '`'; + var requested = '`' + requestedName + '`'; + console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!'); + } + return isRequired; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function arrow(data, options) { + var _data$offsets$arrow; + + // arrow depends on keepTogether in order to work + if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) { + return data; + } + + var arrowElement = options.element; + + // if arrowElement is a string, suppose it's a CSS selector + if (typeof arrowElement === 'string') { + arrowElement = data.instance.popper.querySelector(arrowElement); + + // if arrowElement is not found, don't run the modifier + if (!arrowElement) { + return data; + } + } else { + // if the arrowElement isn't a query selector we must check that the + // provided DOM node is child of its popper node + if (!data.instance.popper.contains(arrowElement)) { + console.warn('WARNING: `arrow.element` must be child of its popper element!'); + return data; + } + } + + var placement = data.placement.split('-')[0]; + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var isVertical = ['left', 'right'].indexOf(placement) !== -1; + + var len = isVertical ? 'height' : 'width'; + var sideCapitalized = isVertical ? 'Top' : 'Left'; + var side = sideCapitalized.toLowerCase(); + var altSide = isVertical ? 'left' : 'top'; + var opSide = isVertical ? 'bottom' : 'right'; + var arrowElementSize = getOuterSizes(arrowElement)[len]; + + // + // extends keepTogether behavior making sure the popper and its + // reference have enough pixels in conjunction + // + + // top/left side + if (reference[opSide] - arrowElementSize < popper[side]) { + data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize); + } + // bottom/right side + if (reference[side] + arrowElementSize > popper[opSide]) { + data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide]; + } + data.offsets.popper = getClientRect(data.offsets.popper); + + // compute center of the popper + var center = reference[side] + reference[len] / 2 - arrowElementSize / 2; + + // Compute the sideValue using the updated popper offsets + // take popper margin in account because we don't have this info available + var css = getStyleComputedProperty(data.instance.popper); + var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10); + var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10); + var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide; + + // prevent arrowElement from being placed not contiguously to its popper + sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0); + + data.arrowElement = arrowElement; + data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow); + + return data; + } + + /** + * Get the opposite placement variation of the given one + * @method + * @memberof Popper.Utils + * @argument {String} placement variation + * @returns {String} flipped placement variation + */ + function getOppositeVariation(variation) { + if (variation === 'end') { + return 'start'; + } else if (variation === 'start') { + return 'end'; + } + return variation; + } + + /** + * List of accepted placements to use as values of the `placement` option.
+ * Valid placements are: + * - `auto` + * - `top` + * - `right` + * - `bottom` + * - `left` + * + * Each placement can have a variation from this list: + * - `-start` + * - `-end` + * + * Variations are interpreted easily if you think of them as the left to right + * written languages. Horizontally (`top` and `bottom`), `start` is left and `end` + * is right.
+ * Vertically (`left` and `right`), `start` is top and `end` is bottom. + * + * Some valid examples are: + * - `top-end` (on top of reference, right aligned) + * - `right-start` (on right of reference, top aligned) + * - `bottom` (on bottom, centered) + * - `auto-end` (on the side with more space available, alignment depends by placement) + * + * @static + * @type {Array} + * @enum {String} + * @readonly + * @method placements + * @memberof Popper + */ + var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start']; + + // Get rid of `auto` `auto-start` and `auto-end` + var validPlacements = placements.slice(3); + + /** + * Given an initial placement, returns all the subsequent placements + * clockwise (or counter-clockwise). + * + * @method + * @memberof Popper.Utils + * @argument {String} placement - A valid placement (it accepts variations) + * @argument {Boolean} counter - Set to true to walk the placements counterclockwise + * @returns {Array} placements including their variations + */ + function clockwise(placement) { + var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var index = validPlacements.indexOf(placement); + var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index)); + return counter ? arr.reverse() : arr; + } + + var BEHAVIORS = { + FLIP: 'flip', + CLOCKWISE: 'clockwise', + COUNTERCLOCKWISE: 'counterclockwise' + }; + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function flip(data, options) { + // if `inner` modifier is enabled, we can't use the `flip` modifier + if (isModifierEnabled(data.instance.modifiers, 'inner')) { + return data; + } + + if (data.flipped && data.placement === data.originalPlacement) { + // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides + return data; + } + + var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed); + + var placement = data.placement.split('-')[0]; + var placementOpposite = getOppositePlacement(placement); + var variation = data.placement.split('-')[1] || ''; + + var flipOrder = []; + + switch (options.behavior) { + case BEHAVIORS.FLIP: + flipOrder = [placement, placementOpposite]; + break; + case BEHAVIORS.CLOCKWISE: + flipOrder = clockwise(placement); + break; + case BEHAVIORS.COUNTERCLOCKWISE: + flipOrder = clockwise(placement, true); + break; + default: + flipOrder = options.behavior; + } + + flipOrder.forEach(function (step, index) { + if (placement !== step || flipOrder.length === index + 1) { + return data; + } + + placement = data.placement.split('-')[0]; + placementOpposite = getOppositePlacement(placement); + + var popperOffsets = data.offsets.popper; + var refOffsets = data.offsets.reference; + + // using floor because the reference offsets may contain decimals we are not going to consider here + var floor = Math.floor; + var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom); + + var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left); + var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right); + var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top); + var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom); + + var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom; + + // flip the variation if required + var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; + var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom); + + if (overlapsRef || overflowsBoundaries || flippedVariation) { + // this boolean to detect any flip loop + data.flipped = true; + + if (overlapsRef || overflowsBoundaries) { + placement = flipOrder[index + 1]; + } + + if (flippedVariation) { + variation = getOppositeVariation(variation); + } + + data.placement = placement + (variation ? '-' + variation : ''); + + // this object contains `position`, we want to preserve it along with + // any additional property we may add in the future + data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement)); + + data = runModifiers(data.instance.modifiers, data, 'flip'); + } + }); + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function keepTogether(data) { + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var placement = data.placement.split('-')[0]; + var floor = Math.floor; + var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; + var side = isVertical ? 'right' : 'bottom'; + var opSide = isVertical ? 'left' : 'top'; + var measurement = isVertical ? 'width' : 'height'; + + if (popper[side] < floor(reference[opSide])) { + data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement]; + } + if (popper[opSide] > floor(reference[side])) { + data.offsets.popper[opSide] = floor(reference[side]); + } + + return data; + } + + /** + * Converts a string containing value + unit into a px value number + * @function + * @memberof {modifiers~offset} + * @private + * @argument {String} str - Value + unit string + * @argument {String} measurement - `height` or `width` + * @argument {Object} popperOffsets + * @argument {Object} referenceOffsets + * @returns {Number|String} + * Value in pixels, or original string if no values were extracted + */ + function toValue(str, measurement, popperOffsets, referenceOffsets) { + // separate value from unit + var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/); + var value = +split[1]; + var unit = split[2]; + + // If it's not a number it's an operator, I guess + if (!value) { + return str; + } + + if (unit.indexOf('%') === 0) { + var element = void 0; + switch (unit) { + case '%p': + element = popperOffsets; + break; + case '%': + case '%r': + default: + element = referenceOffsets; + } + + var rect = getClientRect(element); + return rect[measurement] / 100 * value; + } else if (unit === 'vh' || unit === 'vw') { + // if is a vh or vw, we calculate the size based on the viewport + var size = void 0; + if (unit === 'vh') { + size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + } else { + size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); + } + return size / 100 * value; + } else { + // if is an explicit pixel unit, we get rid of the unit and keep the value + // if is an implicit unit, it's px, and we return just the value + return value; + } + } + + /** + * Parse an `offset` string to extrapolate `x` and `y` numeric offsets. + * @function + * @memberof {modifiers~offset} + * @private + * @argument {String} offset + * @argument {Object} popperOffsets + * @argument {Object} referenceOffsets + * @argument {String} basePlacement + * @returns {Array} a two cells array with x and y offsets in numbers + */ + function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) { + var offsets = [0, 0]; + + // Use height if placement is left or right and index is 0 otherwise use width + // in this way the first offset will use an axis and the second one + // will use the other one + var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1; + + // Split the offset string to obtain a list of values and operands + // The regex addresses values with the plus or minus sign in front (+10, -20, etc) + var fragments = offset.split(/(\+|\-)/).map(function (frag) { + return frag.trim(); + }); + + // Detect if the offset string contains a pair of values or a single one + // they could be separated by comma or space + var divider = fragments.indexOf(find(fragments, function (frag) { + return frag.search(/,|\s/) !== -1; + })); + + if (fragments[divider] && fragments[divider].indexOf(',') === -1) { + console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.'); + } + + // If divider is found, we divide the list of values and operands to divide + // them by ofset X and Y. + var splitRegex = /\s*,\s*|\s+/; + var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments]; + + // Convert the values with units to absolute pixels to allow our computations + ops = ops.map(function (op, index) { + // Most of the units rely on the orientation of the popper + var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width'; + var mergeWithPrevious = false; + return op + // This aggregates any `+` or `-` sign that aren't considered operators + // e.g.: 10 + +5 => [10, +, +5] + .reduce(function (a, b) { + if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) { + a[a.length - 1] = b; + mergeWithPrevious = true; + return a; + } else if (mergeWithPrevious) { + a[a.length - 1] += b; + mergeWithPrevious = false; + return a; + } else { + return a.concat(b); + } + }, []) + // Here we convert the string values into number values (in px) + .map(function (str) { + return toValue(str, measurement, popperOffsets, referenceOffsets); + }); + }); + + // Loop trough the offsets arrays and execute the operations + ops.forEach(function (op, index) { + op.forEach(function (frag, index2) { + if (isNumeric(frag)) { + offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1); + } + }); + }); + return offsets; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @argument {Number|String} options.offset=0 + * The offset value as described in the modifier description + * @returns {Object} The data object, properly modified + */ + function offset(data, _ref) { + var offset = _ref.offset; + var placement = data.placement, + _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var basePlacement = placement.split('-')[0]; + + var offsets = void 0; + if (isNumeric(+offset)) { + offsets = [+offset, 0]; + } else { + offsets = parseOffset(offset, popper, reference, basePlacement); + } + + if (basePlacement === 'left') { + popper.top += offsets[0]; + popper.left -= offsets[1]; + } else if (basePlacement === 'right') { + popper.top += offsets[0]; + popper.left += offsets[1]; + } else if (basePlacement === 'top') { + popper.left += offsets[0]; + popper.top -= offsets[1]; + } else if (basePlacement === 'bottom') { + popper.left += offsets[0]; + popper.top += offsets[1]; + } + + data.popper = popper; + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function preventOverflow(data, options) { + var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper); + + // If offsetParent is the reference element, we really want to + // go one step up and use the next offsetParent as reference to + // avoid to make this modifier completely useless and look like broken + if (data.instance.reference === boundariesElement) { + boundariesElement = getOffsetParent(boundariesElement); + } + + // NOTE: DOM access here + // resets the popper's position so that the document size can be calculated excluding + // the size of the popper element itself + var transformProp = getSupportedPropertyName('transform'); + var popperStyles = data.instance.popper.style; // assignment to help minification + var top = popperStyles.top, + left = popperStyles.left, + transform = popperStyles[transformProp]; + + popperStyles.top = ''; + popperStyles.left = ''; + popperStyles[transformProp] = ''; + + var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed); + + // NOTE: DOM access here + // restores the original style properties after the offsets have been computed + popperStyles.top = top; + popperStyles.left = left; + popperStyles[transformProp] = transform; + + options.boundaries = boundaries; + + var order = options.priority; + var popper = data.offsets.popper; + + var check = { + primary: function primary(placement) { + var value = popper[placement]; + if (popper[placement] < boundaries[placement] && !options.escapeWithReference) { + value = Math.max(popper[placement], boundaries[placement]); + } + return defineProperty({}, placement, value); + }, + secondary: function secondary(placement) { + var mainSide = placement === 'right' ? 'left' : 'top'; + var value = popper[mainSide]; + if (popper[placement] > boundaries[placement] && !options.escapeWithReference) { + value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height)); + } + return defineProperty({}, mainSide, value); + } + }; + + order.forEach(function (placement) { + var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary'; + popper = _extends({}, popper, check[side](placement)); + }); + + data.offsets.popper = popper; + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function shift(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var shiftvariation = placement.split('-')[1]; + + // if shift shiftvariation is specified, run the modifier + if (shiftvariation) { + var _data$offsets = data.offsets, + reference = _data$offsets.reference, + popper = _data$offsets.popper; + + var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1; + var side = isVertical ? 'left' : 'top'; + var measurement = isVertical ? 'width' : 'height'; + + var shiftOffsets = { + start: defineProperty({}, side, reference[side]), + end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement]) + }; + + data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]); + } + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function hide(data) { + if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) { + return data; + } + + var refRect = data.offsets.reference; + var bound = find(data.instance.modifiers, function (modifier) { + return modifier.name === 'preventOverflow'; + }).boundaries; + + if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) { + // Avoid unnecessary DOM access if visibility hasn't changed + if (data.hide === true) { + return data; + } + + data.hide = true; + data.attributes['x-out-of-boundaries'] = ''; + } else { + // Avoid unnecessary DOM access if visibility hasn't changed + if (data.hide === false) { + return data; + } + + data.hide = false; + data.attributes['x-out-of-boundaries'] = false; + } + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function inner(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1; + + var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1; + + popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0); + + data.placement = getOppositePlacement(placement); + data.offsets.popper = getClientRect(popper); + + return data; + } + + /** + * Modifier function, each modifier can have a function of this type assigned + * to its `fn` property.
+ * These functions will be called on each update, this means that you must + * make sure they are performant enough to avoid performance bottlenecks. + * + * @function ModifierFn + * @argument {dataObject} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {dataObject} The data object, properly modified + */ + + /** + * Modifiers are plugins used to alter the behavior of your poppers.
+ * Popper.js uses a set of 9 modifiers to provide all the basic functionalities + * needed by the library. + * + * Usually you don't want to override the `order`, `fn` and `onLoad` props. + * All the other properties are configurations that could be tweaked. + * @namespace modifiers + */ + var modifiers = { + /** + * Modifier used to shift the popper on the start or end of its reference + * element.
+ * It will read the variation of the `placement` property.
+ * It can be one either `-end` or `-start`. + * @memberof modifiers + * @inner + */ + shift: { + /** @prop {number} order=100 - Index used to define the order of execution */ + order: 100, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: shift + }, + + /** + * The `offset` modifier can shift your popper on both its axis. + * + * It accepts the following units: + * - `px` or unit-less, interpreted as pixels + * - `%` or `%r`, percentage relative to the length of the reference element + * - `%p`, percentage relative to the length of the popper element + * - `vw`, CSS viewport width unit + * - `vh`, CSS viewport height unit + * + * For length is intended the main axis relative to the placement of the popper.
+ * This means that if the placement is `top` or `bottom`, the length will be the + * `width`. In case of `left` or `right`, it will be the `height`. + * + * You can provide a single value (as `Number` or `String`), or a pair of values + * as `String` divided by a comma or one (or more) white spaces.
+ * The latter is a deprecated method because it leads to confusion and will be + * removed in v2.
+ * Additionally, it accepts additions and subtractions between different units. + * Note that multiplications and divisions aren't supported. + * + * Valid examples are: + * ``` + * 10 + * '10%' + * '10, 10' + * '10%, 10' + * '10 + 10%' + * '10 - 5vh + 3%' + * '-10px + 5vh, 5px - 6%' + * ``` + * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap + * > with their reference element, unfortunately, you will have to disable the `flip` modifier. + * > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373). + * + * @memberof modifiers + * @inner + */ + offset: { + /** @prop {number} order=200 - Index used to define the order of execution */ + order: 200, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: offset, + /** @prop {Number|String} offset=0 + * The offset value as described in the modifier description + */ + offset: 0 + }, + + /** + * Modifier used to prevent the popper from being positioned outside the boundary. + * + * A scenario exists where the reference itself is not within the boundaries.
+ * We can say it has "escaped the boundaries" — or just "escaped".
+ * In this case we need to decide whether the popper should either: + * + * - detach from the reference and remain "trapped" in the boundaries, or + * - if it should ignore the boundary and "escape with its reference" + * + * When `escapeWithReference` is set to`true` and reference is completely + * outside its boundaries, the popper will overflow (or completely leave) + * the boundaries in order to remain attached to the edge of the reference. + * + * @memberof modifiers + * @inner + */ + preventOverflow: { + /** @prop {number} order=300 - Index used to define the order of execution */ + order: 300, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: preventOverflow, + /** + * @prop {Array} [priority=['left','right','top','bottom']] + * Popper will try to prevent overflow following these priorities by default, + * then, it could overflow on the left and on top of the `boundariesElement` + */ + priority: ['left', 'right', 'top', 'bottom'], + /** + * @prop {number} padding=5 + * Amount of pixel used to define a minimum distance between the boundaries + * and the popper. This makes sure the popper always has a little padding + * between the edges of its container + */ + padding: 5, + /** + * @prop {String|HTMLElement} boundariesElement='scrollParent' + * Boundaries used by the modifier. Can be `scrollParent`, `window`, + * `viewport` or any DOM element. + */ + boundariesElement: 'scrollParent' + }, + + /** + * Modifier used to make sure the reference and its popper stay near each other + * without leaving any gap between the two. Especially useful when the arrow is + * enabled and you want to ensure that it points to its reference element. + * It cares only about the first axis. You can still have poppers with margin + * between the popper and its reference element. + * @memberof modifiers + * @inner + */ + keepTogether: { + /** @prop {number} order=400 - Index used to define the order of execution */ + order: 400, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: keepTogether + }, + + /** + * This modifier is used to move the `arrowElement` of the popper to make + * sure it is positioned between the reference element and its popper element. + * It will read the outer size of the `arrowElement` node to detect how many + * pixels of conjunction are needed. + * + * It has no effect if no `arrowElement` is provided. + * @memberof modifiers + * @inner + */ + arrow: { + /** @prop {number} order=500 - Index used to define the order of execution */ + order: 500, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: arrow, + /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */ + element: '[x-arrow]' + }, + + /** + * Modifier used to flip the popper's placement when it starts to overlap its + * reference element. + * + * Requires the `preventOverflow` modifier before it in order to work. + * + * **NOTE:** this modifier will interrupt the current update cycle and will + * restart it if it detects the need to flip the placement. + * @memberof modifiers + * @inner + */ + flip: { + /** @prop {number} order=600 - Index used to define the order of execution */ + order: 600, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: flip, + /** + * @prop {String|Array} behavior='flip' + * The behavior used to change the popper's placement. It can be one of + * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid + * placements (with optional variations) + */ + behavior: 'flip', + /** + * @prop {number} padding=5 + * The popper will flip if it hits the edges of the `boundariesElement` + */ + padding: 5, + /** + * @prop {String|HTMLElement} boundariesElement='viewport' + * The element which will define the boundaries of the popper position. + * The popper will never be placed outside of the defined boundaries + * (except if `keepTogether` is enabled) + */ + boundariesElement: 'viewport' + }, + + /** + * Modifier used to make the popper flow toward the inner of the reference element. + * By default, when this modifier is disabled, the popper will be placed outside + * the reference element. + * @memberof modifiers + * @inner + */ + inner: { + /** @prop {number} order=700 - Index used to define the order of execution */ + order: 700, + /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */ + enabled: false, + /** @prop {ModifierFn} */ + fn: inner + }, + + /** + * Modifier used to hide the popper when its reference element is outside of the + * popper boundaries. It will set a `x-out-of-boundaries` attribute which can + * be used to hide with a CSS selector the popper when its reference is + * out of boundaries. + * + * Requires the `preventOverflow` modifier before it in order to work. + * @memberof modifiers + * @inner + */ + hide: { + /** @prop {number} order=800 - Index used to define the order of execution */ + order: 800, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: hide + }, + + /** + * Computes the style that will be applied to the popper element to gets + * properly positioned. + * + * Note that this modifier will not touch the DOM, it just prepares the styles + * so that `applyStyle` modifier can apply it. This separation is useful + * in case you need to replace `applyStyle` with a custom implementation. + * + * This modifier has `850` as `order` value to maintain backward compatibility + * with previous versions of Popper.js. Expect the modifiers ordering method + * to change in future major versions of the library. + * + * @memberof modifiers + * @inner + */ + computeStyle: { + /** @prop {number} order=850 - Index used to define the order of execution */ + order: 850, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: computeStyle, + /** + * @prop {Boolean} gpuAcceleration=true + * If true, it uses the CSS 3D transformation to position the popper. + * Otherwise, it will use the `top` and `left` properties + */ + gpuAcceleration: true, + /** + * @prop {string} [x='bottom'] + * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin. + * Change this if your popper should grow in a direction different from `bottom` + */ + x: 'bottom', + /** + * @prop {string} [x='left'] + * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin. + * Change this if your popper should grow in a direction different from `right` + */ + y: 'right' + }, + + /** + * Applies the computed styles to the popper element. + * + * All the DOM manipulations are limited to this modifier. This is useful in case + * you want to integrate Popper.js inside a framework or view library and you + * want to delegate all the DOM manipulations to it. + * + * Note that if you disable this modifier, you must make sure the popper element + * has its position set to `absolute` before Popper.js can do its work! + * + * Just disable this modifier and define your own to achieve the desired effect. + * + * @memberof modifiers + * @inner + */ + applyStyle: { + /** @prop {number} order=900 - Index used to define the order of execution */ + order: 900, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: applyStyle, + /** @prop {Function} */ + onLoad: applyStyleOnLoad, + /** + * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier + * @prop {Boolean} gpuAcceleration=true + * If true, it uses the CSS 3D transformation to position the popper. + * Otherwise, it will use the `top` and `left` properties + */ + gpuAcceleration: undefined + } + }; + + /** + * The `dataObject` is an object containing all the information used by Popper.js. + * This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks. + * @name dataObject + * @property {Object} data.instance The Popper.js instance + * @property {String} data.placement Placement applied to popper + * @property {String} data.originalPlacement Placement originally defined on init + * @property {Boolean} data.flipped True if popper has been flipped by flip modifier + * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper + * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier + * @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`) + * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`) + * @property {Object} data.boundaries Offsets of the popper boundaries + * @property {Object} data.offsets The measurements of popper, reference and arrow elements + * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values + * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values + * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0 + */ + + /** + * Default options provided to Popper.js constructor.
+ * These can be overridden using the `options` argument of Popper.js.
+ * To override an option, simply pass an object with the same + * structure of the `options` object, as the 3rd argument. For example: + * ``` + * new Popper(ref, pop, { + * modifiers: { + * preventOverflow: { enabled: false } + * } + * }) + * ``` + * @type {Object} + * @static + * @memberof Popper + */ + var Defaults = { + /** + * Popper's placement. + * @prop {Popper.placements} placement='bottom' + */ + placement: 'bottom', + + /** + * Set this to true if you want popper to position it self in 'fixed' mode + * @prop {Boolean} positionFixed=false + */ + positionFixed: false, + + /** + * Whether events (resize, scroll) are initially enabled. + * @prop {Boolean} eventsEnabled=true + */ + eventsEnabled: true, + + /** + * Set to true if you want to automatically remove the popper when + * you call the `destroy` method. + * @prop {Boolean} removeOnDestroy=false + */ + removeOnDestroy: false, + + /** + * Callback called when the popper is created.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`. + * @prop {onCreate} + */ + onCreate: function onCreate() {}, + + /** + * Callback called when the popper is updated. This callback is not called + * on the initialization/creation of the popper, but only on subsequent + * updates.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`. + * @prop {onUpdate} + */ + onUpdate: function onUpdate() {}, + + /** + * List of modifiers used to modify the offsets before they are applied to the popper. + * They provide most of the functionalities of Popper.js. + * @prop {modifiers} + */ + modifiers: modifiers + }; + + /** + * @callback onCreate + * @param {dataObject} data + */ + + /** + * @callback onUpdate + * @param {dataObject} data + */ + + // Utils + // Methods + var Popper = function () { + /** + * Creates a new Popper.js instance. + * @class Popper + * @param {HTMLElement|referenceObject} reference - The reference element used to position the popper + * @param {HTMLElement} popper - The HTML element used as the popper + * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults) + * @return {Object} instance - The generated Popper.js instance + */ + function Popper(reference, popper) { + var _this = this; + + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + classCallCheck(this, Popper); + + this.scheduleUpdate = function () { + return requestAnimationFrame(_this.update); + }; + + // make update() debounced, so that it only runs at most once-per-tick + this.update = debounce(this.update.bind(this)); + + // with {} we create a new object with the options inside it + this.options = _extends({}, Popper.Defaults, options); + + // init state + this.state = { + isDestroyed: false, + isCreated: false, + scrollParents: [] + }; + + // get reference and popper elements (allow jQuery wrappers) + this.reference = reference && reference.jquery ? reference[0] : reference; + this.popper = popper && popper.jquery ? popper[0] : popper; + + // Deep merge modifiers options + this.options.modifiers = {}; + Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) { + _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {}); + }); + + // Refactoring modifiers' list (Object => Array) + this.modifiers = Object.keys(this.options.modifiers).map(function (name) { + return _extends({ + name: name + }, _this.options.modifiers[name]); + }) + // sort the modifiers by order + .sort(function (a, b) { + return a.order - b.order; + }); + + // modifiers have the ability to execute arbitrary code when Popper.js get inited + // such code is executed in the same order of its modifier + // they could add new properties to their options configuration + // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`! + this.modifiers.forEach(function (modifierOptions) { + if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) { + modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state); + } + }); + + // fire the first update to position the popper in the right place + this.update(); + + var eventsEnabled = this.options.eventsEnabled; + if (eventsEnabled) { + // setup event listeners, they will take care of update the position in specific situations + this.enableEventListeners(); + } + + this.state.eventsEnabled = eventsEnabled; + } + + // We can't use class properties because they don't get listed in the + // class prototype and break stuff like Sinon stubs + + + createClass(Popper, [{ + key: 'update', + value: function update$$1() { + return update.call(this); + } + }, { + key: 'destroy', + value: function destroy$$1() { + return destroy.call(this); + } + }, { + key: 'enableEventListeners', + value: function enableEventListeners$$1() { + return enableEventListeners.call(this); + } + }, { + key: 'disableEventListeners', + value: function disableEventListeners$$1() { + return disableEventListeners.call(this); + } + + /** + * Schedules an update. It will run on the next UI update available. + * @method scheduleUpdate + * @memberof Popper + */ + + + /** + * Collection of utilities useful when writing custom modifiers. + * Starting from version 1.7, this method is available only if you + * include `popper-utils.js` before `popper.js`. + * + * **DEPRECATION**: This way to access PopperUtils is deprecated + * and will be removed in v2! Use the PopperUtils module directly instead. + * Due to the high instability of the methods contained in Utils, we can't + * guarantee them to follow semver. Use them at your own risk! + * @static + * @private + * @type {Object} + * @deprecated since version 1.8 + * @member Utils + * @memberof Popper + */ + + }]); + return Popper; + }(); + + /** + * The `referenceObject` is an object that provides an interface compatible with Popper.js + * and lets you use it as replacement of a real DOM node.
+ * You can use this method to position a popper relatively to a set of coordinates + * in case you don't have a DOM node to use as reference. + * + * ``` + * new Popper(referenceObject, popperNode); + * ``` + * + * NB: This feature isn't supported in Internet Explorer 10. + * @name referenceObject + * @property {Function} data.getBoundingClientRect + * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method. + * @property {number} data.clientWidth + * An ES6 getter that will return the width of the virtual reference element. + * @property {number} data.clientHeight + * An ES6 getter that will return the height of the virtual reference element. + */ + + + Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils; + Popper.placements = placements; + Popper.Defaults = Defaults; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$4 = 'dropdown'; + var VERSION$4 = '4.3.1'; + var DATA_KEY$4 = 'bs.dropdown'; + var EVENT_KEY$4 = "." + DATA_KEY$4; + var DATA_API_KEY$4 = '.data-api'; + var JQUERY_NO_CONFLICT$4 = $.fn[NAME$4]; + var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key + + var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key + + var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key + + var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key + + var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key + + var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse) + + var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE); + var Event$4 = { + HIDE: "hide" + EVENT_KEY$4, + HIDDEN: "hidden" + EVENT_KEY$4, + SHOW: "show" + EVENT_KEY$4, + SHOWN: "shown" + EVENT_KEY$4, + CLICK: "click" + EVENT_KEY$4, + CLICK_DATA_API: "click" + EVENT_KEY$4 + DATA_API_KEY$4, + KEYDOWN_DATA_API: "keydown" + EVENT_KEY$4 + DATA_API_KEY$4, + KEYUP_DATA_API: "keyup" + EVENT_KEY$4 + DATA_API_KEY$4 + }; + var ClassName$4 = { + DISABLED: 'disabled', + SHOW: 'show', + DROPUP: 'dropup', + DROPRIGHT: 'dropright', + DROPLEFT: 'dropleft', + MENURIGHT: 'dropdown-menu-right', + MENULEFT: 'dropdown-menu-left', + POSITION_STATIC: 'position-static' + }; + var Selector$4 = { + DATA_TOGGLE: '[data-toggle="dropdown"]', + FORM_CHILD: '.dropdown form', + MENU: '.dropdown-menu', + NAVBAR_NAV: '.navbar-nav', + VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)' + }; + var AttachmentMap = { + TOP: 'top-start', + TOPEND: 'top-end', + BOTTOM: 'bottom-start', + BOTTOMEND: 'bottom-end', + RIGHT: 'right-start', + RIGHTEND: 'right-end', + LEFT: 'left-start', + LEFTEND: 'left-end' + }; + var Default$2 = { + offset: 0, + flip: true, + boundary: 'scrollParent', + reference: 'toggle', + display: 'dynamic' + }; + var DefaultType$2 = { + offset: '(number|string|function)', + flip: 'boolean', + boundary: '(string|element)', + reference: '(string|element)', + display: 'string' + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Dropdown = + /*#__PURE__*/ + function () { + function Dropdown(element, config) { + this._element = element; + this._popper = null; + this._config = this._getConfig(config); + this._menu = this._getMenuElement(); + this._inNavbar = this._detectNavbar(); + + this._addEventListeners(); + } // Getters + + + var _proto = Dropdown.prototype; + + // Public + _proto.toggle = function toggle() { + if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED)) { + return; + } + + var parent = Dropdown._getParentFromElement(this._element); + + var isActive = $(this._menu).hasClass(ClassName$4.SHOW); + + Dropdown._clearMenus(); + + if (isActive) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var showEvent = $.Event(Event$4.SHOW, relatedTarget); + $(parent).trigger(showEvent); + + if (showEvent.isDefaultPrevented()) { + return; + } // Disable totally Popper.js for Dropdown in Navbar + + + if (!this._inNavbar) { + /** + * Check for Popper dependency + * Popper - https://popper.js.org + */ + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org/)'); + } + + var referenceElement = this._element; + + if (this._config.reference === 'parent') { + referenceElement = parent; + } else if (Util.isElement(this._config.reference)) { + referenceElement = this._config.reference; // Check if it's jQuery element + + if (typeof this._config.reference.jquery !== 'undefined') { + referenceElement = this._config.reference[0]; + } + } // If boundary is not `scrollParent`, then set position to `static` + // to allow the menu to "escape" the scroll parent's boundaries + // https://github.com/twbs/bootstrap/issues/24251 + + + if (this._config.boundary !== 'scrollParent') { + $(parent).addClass(ClassName$4.POSITION_STATIC); + } + + this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()); + } // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + + + if ('ontouchstart' in document.documentElement && $(parent).closest(Selector$4.NAVBAR_NAV).length === 0) { + $(document.body).children().on('mouseover', null, $.noop); + } + + this._element.focus(); + + this._element.setAttribute('aria-expanded', true); + + $(this._menu).toggleClass(ClassName$4.SHOW); + $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.SHOWN, relatedTarget)); + }; + + _proto.show = function show() { + if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED) || $(this._menu).hasClass(ClassName$4.SHOW)) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var showEvent = $.Event(Event$4.SHOW, relatedTarget); + + var parent = Dropdown._getParentFromElement(this._element); + + $(parent).trigger(showEvent); + + if (showEvent.isDefaultPrevented()) { + return; + } + + $(this._menu).toggleClass(ClassName$4.SHOW); + $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.SHOWN, relatedTarget)); + }; + + _proto.hide = function hide() { + if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED) || !$(this._menu).hasClass(ClassName$4.SHOW)) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var hideEvent = $.Event(Event$4.HIDE, relatedTarget); + + var parent = Dropdown._getParentFromElement(this._element); + + $(parent).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + return; + } + + $(this._menu).toggleClass(ClassName$4.SHOW); + $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.HIDDEN, relatedTarget)); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$4); + $(this._element).off(EVENT_KEY$4); + this._element = null; + this._menu = null; + + if (this._popper !== null) { + this._popper.destroy(); + + this._popper = null; + } + }; + + _proto.update = function update() { + this._inNavbar = this._detectNavbar(); + + if (this._popper !== null) { + this._popper.scheduleUpdate(); + } + } // Private + ; + + _proto._addEventListeners = function _addEventListeners() { + var _this = this; + + $(this._element).on(Event$4.CLICK, function (event) { + event.preventDefault(); + event.stopPropagation(); + + _this.toggle(); + }); + }; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread({}, this.constructor.Default, $(this._element).data(), config); + Util.typeCheckConfig(NAME$4, config, this.constructor.DefaultType); + return config; + }; + + _proto._getMenuElement = function _getMenuElement() { + if (!this._menu) { + var parent = Dropdown._getParentFromElement(this._element); + + if (parent) { + this._menu = parent.querySelector(Selector$4.MENU); + } + } + + return this._menu; + }; + + _proto._getPlacement = function _getPlacement() { + var $parentDropdown = $(this._element.parentNode); + var placement = AttachmentMap.BOTTOM; // Handle dropup + + if ($parentDropdown.hasClass(ClassName$4.DROPUP)) { + placement = AttachmentMap.TOP; + + if ($(this._menu).hasClass(ClassName$4.MENURIGHT)) { + placement = AttachmentMap.TOPEND; + } + } else if ($parentDropdown.hasClass(ClassName$4.DROPRIGHT)) { + placement = AttachmentMap.RIGHT; + } else if ($parentDropdown.hasClass(ClassName$4.DROPLEFT)) { + placement = AttachmentMap.LEFT; + } else if ($(this._menu).hasClass(ClassName$4.MENURIGHT)) { + placement = AttachmentMap.BOTTOMEND; + } + + return placement; + }; + + _proto._detectNavbar = function _detectNavbar() { + return $(this._element).closest('.navbar').length > 0; + }; + + _proto._getOffset = function _getOffset() { + var _this2 = this; + + var offset = {}; + + if (typeof this._config.offset === 'function') { + offset.fn = function (data) { + data.offsets = _objectSpread({}, data.offsets, _this2._config.offset(data.offsets, _this2._element) || {}); + return data; + }; + } else { + offset.offset = this._config.offset; + } + + return offset; + }; + + _proto._getPopperConfig = function _getPopperConfig() { + var popperConfig = { + placement: this._getPlacement(), + modifiers: { + offset: this._getOffset(), + flip: { + enabled: this._config.flip + }, + preventOverflow: { + boundariesElement: this._config.boundary + } + } // Disable Popper.js if we have a static display + + }; + + if (this._config.display === 'static') { + popperConfig.modifiers.applyStyle = { + enabled: false + }; + } + + return popperConfig; + } // Static + ; + + Dropdown._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$4); + + var _config = typeof config === 'object' ? config : null; + + if (!data) { + data = new Dropdown(this, _config); + $(this).data(DATA_KEY$4, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + Dropdown._clearMenus = function _clearMenus(event) { + if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) { + return; + } + + var toggles = [].slice.call(document.querySelectorAll(Selector$4.DATA_TOGGLE)); + + for (var i = 0, len = toggles.length; i < len; i++) { + var parent = Dropdown._getParentFromElement(toggles[i]); + + var context = $(toggles[i]).data(DATA_KEY$4); + var relatedTarget = { + relatedTarget: toggles[i] + }; + + if (event && event.type === 'click') { + relatedTarget.clickEvent = event; + } + + if (!context) { + continue; + } + + var dropdownMenu = context._menu; + + if (!$(parent).hasClass(ClassName$4.SHOW)) { + continue; + } + + if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $.contains(parent, event.target)) { + continue; + } + + var hideEvent = $.Event(Event$4.HIDE, relatedTarget); + $(parent).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + continue; + } // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().off('mouseover', null, $.noop); + } + + toggles[i].setAttribute('aria-expanded', 'false'); + $(dropdownMenu).removeClass(ClassName$4.SHOW); + $(parent).removeClass(ClassName$4.SHOW).trigger($.Event(Event$4.HIDDEN, relatedTarget)); + } + }; + + Dropdown._getParentFromElement = function _getParentFromElement(element) { + var parent; + var selector = Util.getSelectorFromElement(element); + + if (selector) { + parent = document.querySelector(selector); + } + + return parent || element.parentNode; + } // eslint-disable-next-line complexity + ; + + Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) { + // If not input/textarea: + // - And not a key in REGEXP_KEYDOWN => not a dropdown command + // If input/textarea: + // - If space key => not a dropdown command + // - If key is other than escape + // - If key is not up or down => not a dropdown command + // - If trigger inside the menu => not a dropdown command + if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $(event.target).closest(Selector$4.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + if (this.disabled || $(this).hasClass(ClassName$4.DISABLED)) { + return; + } + + var parent = Dropdown._getParentFromElement(this); + + var isActive = $(parent).hasClass(ClassName$4.SHOW); + + if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { + if (event.which === ESCAPE_KEYCODE) { + var toggle = parent.querySelector(Selector$4.DATA_TOGGLE); + $(toggle).trigger('focus'); + } + + $(this).trigger('click'); + return; + } + + var items = [].slice.call(parent.querySelectorAll(Selector$4.VISIBLE_ITEMS)); + + if (items.length === 0) { + return; + } + + var index = items.indexOf(event.target); + + if (event.which === ARROW_UP_KEYCODE && index > 0) { + // Up + index--; + } + + if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { + // Down + index++; + } + + if (index < 0) { + index = 0; + } + + items[index].focus(); + }; + + _createClass(Dropdown, null, [{ + key: "VERSION", + get: function get() { + return VERSION$4; + } + }, { + key: "Default", + get: function get() { + return Default$2; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$2; + } + }]); + + return Dropdown; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(Event$4.KEYDOWN_DATA_API, Selector$4.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event$4.KEYDOWN_DATA_API, Selector$4.MENU, Dropdown._dataApiKeydownHandler).on(Event$4.CLICK_DATA_API + " " + Event$4.KEYUP_DATA_API, Dropdown._clearMenus).on(Event$4.CLICK_DATA_API, Selector$4.DATA_TOGGLE, function (event) { + event.preventDefault(); + event.stopPropagation(); + + Dropdown._jQueryInterface.call($(this), 'toggle'); + }).on(Event$4.CLICK_DATA_API, Selector$4.FORM_CHILD, function (e) { + e.stopPropagation(); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$4] = Dropdown._jQueryInterface; + $.fn[NAME$4].Constructor = Dropdown; + + $.fn[NAME$4].noConflict = function () { + $.fn[NAME$4] = JQUERY_NO_CONFLICT$4; + return Dropdown._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$5 = 'modal'; + var VERSION$5 = '4.3.1'; + var DATA_KEY$5 = 'bs.modal'; + var EVENT_KEY$5 = "." + DATA_KEY$5; + var DATA_API_KEY$5 = '.data-api'; + var JQUERY_NO_CONFLICT$5 = $.fn[NAME$5]; + var ESCAPE_KEYCODE$1 = 27; // KeyboardEvent.which value for Escape (Esc) key + + var Default$3 = { + backdrop: true, + keyboard: true, + focus: true, + show: true + }; + var DefaultType$3 = { + backdrop: '(boolean|string)', + keyboard: 'boolean', + focus: 'boolean', + show: 'boolean' + }; + var Event$5 = { + HIDE: "hide" + EVENT_KEY$5, + HIDDEN: "hidden" + EVENT_KEY$5, + SHOW: "show" + EVENT_KEY$5, + SHOWN: "shown" + EVENT_KEY$5, + FOCUSIN: "focusin" + EVENT_KEY$5, + RESIZE: "resize" + EVENT_KEY$5, + CLICK_DISMISS: "click.dismiss" + EVENT_KEY$5, + KEYDOWN_DISMISS: "keydown.dismiss" + EVENT_KEY$5, + MOUSEUP_DISMISS: "mouseup.dismiss" + EVENT_KEY$5, + MOUSEDOWN_DISMISS: "mousedown.dismiss" + EVENT_KEY$5, + CLICK_DATA_API: "click" + EVENT_KEY$5 + DATA_API_KEY$5 + }; + var ClassName$5 = { + SCROLLABLE: 'modal-dialog-scrollable', + SCROLLBAR_MEASURER: 'modal-scrollbar-measure', + BACKDROP: 'modal-backdrop', + OPEN: 'modal-open', + FADE: 'fade', + SHOW: 'show' + }; + var Selector$5 = { + DIALOG: '.modal-dialog', + MODAL_BODY: '.modal-body', + DATA_TOGGLE: '[data-toggle="modal"]', + DATA_DISMISS: '[data-dismiss="modal"]', + FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top', + STICKY_CONTENT: '.sticky-top' + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Modal = + /*#__PURE__*/ + function () { + function Modal(element, config) { + this._config = this._getConfig(config); + this._element = element; + this._dialog = element.querySelector(Selector$5.DIALOG); + this._backdrop = null; + this._isShown = false; + this._isBodyOverflowing = false; + this._ignoreBackdropClick = false; + this._isTransitioning = false; + this._scrollbarWidth = 0; + } // Getters + + + var _proto = Modal.prototype; + + // Public + _proto.toggle = function toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + }; + + _proto.show = function show(relatedTarget) { + var _this = this; + + if (this._isShown || this._isTransitioning) { + return; + } + + if ($(this._element).hasClass(ClassName$5.FADE)) { + this._isTransitioning = true; + } + + var showEvent = $.Event(Event$5.SHOW, { + relatedTarget: relatedTarget + }); + $(this._element).trigger(showEvent); + + if (this._isShown || showEvent.isDefaultPrevented()) { + return; + } + + this._isShown = true; + + this._checkScrollbar(); + + this._setScrollbar(); + + this._adjustDialog(); + + this._setEscapeEvent(); + + this._setResizeEvent(); + + $(this._element).on(Event$5.CLICK_DISMISS, Selector$5.DATA_DISMISS, function (event) { + return _this.hide(event); + }); + $(this._dialog).on(Event$5.MOUSEDOWN_DISMISS, function () { + $(_this._element).one(Event$5.MOUSEUP_DISMISS, function (event) { + if ($(event.target).is(_this._element)) { + _this._ignoreBackdropClick = true; + } + }); + }); + + this._showBackdrop(function () { + return _this._showElement(relatedTarget); + }); + }; + + _proto.hide = function hide(event) { + var _this2 = this; + + if (event) { + event.preventDefault(); + } + + if (!this._isShown || this._isTransitioning) { + return; + } + + var hideEvent = $.Event(Event$5.HIDE); + $(this._element).trigger(hideEvent); + + if (!this._isShown || hideEvent.isDefaultPrevented()) { + return; + } + + this._isShown = false; + var transition = $(this._element).hasClass(ClassName$5.FADE); + + if (transition) { + this._isTransitioning = true; + } + + this._setEscapeEvent(); + + this._setResizeEvent(); + + $(document).off(Event$5.FOCUSIN); + $(this._element).removeClass(ClassName$5.SHOW); + $(this._element).off(Event$5.CLICK_DISMISS); + $(this._dialog).off(Event$5.MOUSEDOWN_DISMISS); + + if (transition) { + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, function (event) { + return _this2._hideModal(event); + }).emulateTransitionEnd(transitionDuration); + } else { + this._hideModal(); + } + }; + + _proto.dispose = function dispose() { + [window, this._element, this._dialog].forEach(function (htmlElement) { + return $(htmlElement).off(EVENT_KEY$5); + }); + /** + * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API` + * Do not move `document` in `htmlElements` array + * It will remove `Event.CLICK_DATA_API` event that should remain + */ + + $(document).off(Event$5.FOCUSIN); + $.removeData(this._element, DATA_KEY$5); + this._config = null; + this._element = null; + this._dialog = null; + this._backdrop = null; + this._isShown = null; + this._isBodyOverflowing = null; + this._ignoreBackdropClick = null; + this._isTransitioning = null; + this._scrollbarWidth = null; + }; + + _proto.handleUpdate = function handleUpdate() { + this._adjustDialog(); + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread({}, Default$3, config); + Util.typeCheckConfig(NAME$5, config, DefaultType$3); + return config; + }; + + _proto._showElement = function _showElement(relatedTarget) { + var _this3 = this; + + var transition = $(this._element).hasClass(ClassName$5.FADE); + + if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { + // Don't move modal's DOM position + document.body.appendChild(this._element); + } + + this._element.style.display = 'block'; + + this._element.removeAttribute('aria-hidden'); + + this._element.setAttribute('aria-modal', true); + + if ($(this._dialog).hasClass(ClassName$5.SCROLLABLE)) { + this._dialog.querySelector(Selector$5.MODAL_BODY).scrollTop = 0; + } else { + this._element.scrollTop = 0; + } + + if (transition) { + Util.reflow(this._element); + } + + $(this._element).addClass(ClassName$5.SHOW); + + if (this._config.focus) { + this._enforceFocus(); + } + + var shownEvent = $.Event(Event$5.SHOWN, { + relatedTarget: relatedTarget + }); + + var transitionComplete = function transitionComplete() { + if (_this3._config.focus) { + _this3._element.focus(); + } + + _this3._isTransitioning = false; + $(_this3._element).trigger(shownEvent); + }; + + if (transition) { + var transitionDuration = Util.getTransitionDurationFromElement(this._dialog); + $(this._dialog).one(Util.TRANSITION_END, transitionComplete).emulateTransitionEnd(transitionDuration); + } else { + transitionComplete(); + } + }; + + _proto._enforceFocus = function _enforceFocus() { + var _this4 = this; + + $(document).off(Event$5.FOCUSIN) // Guard against infinite focus loop + .on(Event$5.FOCUSIN, function (event) { + if (document !== event.target && _this4._element !== event.target && $(_this4._element).has(event.target).length === 0) { + _this4._element.focus(); + } + }); + }; + + _proto._setEscapeEvent = function _setEscapeEvent() { + var _this5 = this; + + if (this._isShown && this._config.keyboard) { + $(this._element).on(Event$5.KEYDOWN_DISMISS, function (event) { + if (event.which === ESCAPE_KEYCODE$1) { + event.preventDefault(); + + _this5.hide(); + } + }); + } else if (!this._isShown) { + $(this._element).off(Event$5.KEYDOWN_DISMISS); + } + }; + + _proto._setResizeEvent = function _setResizeEvent() { + var _this6 = this; + + if (this._isShown) { + $(window).on(Event$5.RESIZE, function (event) { + return _this6.handleUpdate(event); + }); + } else { + $(window).off(Event$5.RESIZE); + } + }; + + _proto._hideModal = function _hideModal() { + var _this7 = this; + + this._element.style.display = 'none'; + + this._element.setAttribute('aria-hidden', true); + + this._element.removeAttribute('aria-modal'); + + this._isTransitioning = false; + + this._showBackdrop(function () { + $(document.body).removeClass(ClassName$5.OPEN); + + _this7._resetAdjustments(); + + _this7._resetScrollbar(); + + $(_this7._element).trigger(Event$5.HIDDEN); + }); + }; + + _proto._removeBackdrop = function _removeBackdrop() { + if (this._backdrop) { + $(this._backdrop).remove(); + this._backdrop = null; + } + }; + + _proto._showBackdrop = function _showBackdrop(callback) { + var _this8 = this; + + var animate = $(this._element).hasClass(ClassName$5.FADE) ? ClassName$5.FADE : ''; + + if (this._isShown && this._config.backdrop) { + this._backdrop = document.createElement('div'); + this._backdrop.className = ClassName$5.BACKDROP; + + if (animate) { + this._backdrop.classList.add(animate); + } + + $(this._backdrop).appendTo(document.body); + $(this._element).on(Event$5.CLICK_DISMISS, function (event) { + if (_this8._ignoreBackdropClick) { + _this8._ignoreBackdropClick = false; + return; + } + + if (event.target !== event.currentTarget) { + return; + } + + if (_this8._config.backdrop === 'static') { + _this8._element.focus(); + } else { + _this8.hide(); + } + }); + + if (animate) { + Util.reflow(this._backdrop); + } + + $(this._backdrop).addClass(ClassName$5.SHOW); + + if (!callback) { + return; + } + + if (!animate) { + callback(); + return; + } + + var backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop); + $(this._backdrop).one(Util.TRANSITION_END, callback).emulateTransitionEnd(backdropTransitionDuration); + } else if (!this._isShown && this._backdrop) { + $(this._backdrop).removeClass(ClassName$5.SHOW); + + var callbackRemove = function callbackRemove() { + _this8._removeBackdrop(); + + if (callback) { + callback(); + } + }; + + if ($(this._element).hasClass(ClassName$5.FADE)) { + var _backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop); + + $(this._backdrop).one(Util.TRANSITION_END, callbackRemove).emulateTransitionEnd(_backdropTransitionDuration); + } else { + callbackRemove(); + } + } else if (callback) { + callback(); + } + } // ---------------------------------------------------------------------- + // the following methods are used to handle overflowing modals + // todo (fat): these should probably be refactored out of modal.js + // ---------------------------------------------------------------------- + ; + + _proto._adjustDialog = function _adjustDialog() { + var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + + if (!this._isBodyOverflowing && isModalOverflowing) { + this._element.style.paddingLeft = this._scrollbarWidth + "px"; + } + + if (this._isBodyOverflowing && !isModalOverflowing) { + this._element.style.paddingRight = this._scrollbarWidth + "px"; + } + }; + + _proto._resetAdjustments = function _resetAdjustments() { + this._element.style.paddingLeft = ''; + this._element.style.paddingRight = ''; + }; + + _proto._checkScrollbar = function _checkScrollbar() { + var rect = document.body.getBoundingClientRect(); + this._isBodyOverflowing = rect.left + rect.right < window.innerWidth; + this._scrollbarWidth = this._getScrollbarWidth(); + }; + + _proto._setScrollbar = function _setScrollbar() { + var _this9 = this; + + if (this._isBodyOverflowing) { + // Note: DOMNode.style.paddingRight returns the actual value or '' if not set + // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set + var fixedContent = [].slice.call(document.querySelectorAll(Selector$5.FIXED_CONTENT)); + var stickyContent = [].slice.call(document.querySelectorAll(Selector$5.STICKY_CONTENT)); // Adjust fixed content padding + + $(fixedContent).each(function (index, element) { + var actualPadding = element.style.paddingRight; + var calculatedPadding = $(element).css('padding-right'); + $(element).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + _this9._scrollbarWidth + "px"); + }); // Adjust sticky content margin + + $(stickyContent).each(function (index, element) { + var actualMargin = element.style.marginRight; + var calculatedMargin = $(element).css('margin-right'); + $(element).data('margin-right', actualMargin).css('margin-right', parseFloat(calculatedMargin) - _this9._scrollbarWidth + "px"); + }); // Adjust body padding + + var actualPadding = document.body.style.paddingRight; + var calculatedPadding = $(document.body).css('padding-right'); + $(document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px"); + } + + $(document.body).addClass(ClassName$5.OPEN); + }; + + _proto._resetScrollbar = function _resetScrollbar() { + // Restore fixed content padding + var fixedContent = [].slice.call(document.querySelectorAll(Selector$5.FIXED_CONTENT)); + $(fixedContent).each(function (index, element) { + var padding = $(element).data('padding-right'); + $(element).removeData('padding-right'); + element.style.paddingRight = padding ? padding : ''; + }); // Restore sticky content + + var elements = [].slice.call(document.querySelectorAll("" + Selector$5.STICKY_CONTENT)); + $(elements).each(function (index, element) { + var margin = $(element).data('margin-right'); + + if (typeof margin !== 'undefined') { + $(element).css('margin-right', margin).removeData('margin-right'); + } + }); // Restore body padding + + var padding = $(document.body).data('padding-right'); + $(document.body).removeData('padding-right'); + document.body.style.paddingRight = padding ? padding : ''; + }; + + _proto._getScrollbarWidth = function _getScrollbarWidth() { + // thx d.walsh + var scrollDiv = document.createElement('div'); + scrollDiv.className = ClassName$5.SCROLLBAR_MEASURER; + document.body.appendChild(scrollDiv); + var scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth; + document.body.removeChild(scrollDiv); + return scrollbarWidth; + } // Static + ; + + Modal._jQueryInterface = function _jQueryInterface(config, relatedTarget) { + return this.each(function () { + var data = $(this).data(DATA_KEY$5); + + var _config = _objectSpread({}, Default$3, $(this).data(), typeof config === 'object' && config ? config : {}); + + if (!data) { + data = new Modal(this, _config); + $(this).data(DATA_KEY$5, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](relatedTarget); + } else if (_config.show) { + data.show(relatedTarget); + } + }); + }; + + _createClass(Modal, null, [{ + key: "VERSION", + get: function get() { + return VERSION$5; + } + }, { + key: "Default", + get: function get() { + return Default$3; + } + }]); + + return Modal; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(Event$5.CLICK_DATA_API, Selector$5.DATA_TOGGLE, function (event) { + var _this10 = this; + + var target; + var selector = Util.getSelectorFromElement(this); + + if (selector) { + target = document.querySelector(selector); + } + + var config = $(target).data(DATA_KEY$5) ? 'toggle' : _objectSpread({}, $(target).data(), $(this).data()); + + if (this.tagName === 'A' || this.tagName === 'AREA') { + event.preventDefault(); + } + + var $target = $(target).one(Event$5.SHOW, function (showEvent) { + if (showEvent.isDefaultPrevented()) { + // Only register focus restorer if modal will actually get shown + return; + } + + $target.one(Event$5.HIDDEN, function () { + if ($(_this10).is(':visible')) { + _this10.focus(); + } + }); + }); + + Modal._jQueryInterface.call($(target), config, this); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$5] = Modal._jQueryInterface; + $.fn[NAME$5].Constructor = Modal; + + $.fn[NAME$5].noConflict = function () { + $.fn[NAME$5] = JQUERY_NO_CONFLICT$5; + return Modal._jQueryInterface; + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap (v4.3.1): tools/sanitizer.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + var uriAttrs = ['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']; + var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; + var DefaultWhitelist = { + // Global attributes allowed on any supplied element below. + '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], + a: ['target', 'href', 'title', 'rel'], + area: [], + b: [], + br: [], + col: [], + code: [], + div: [], + em: [], + hr: [], + h1: [], + h2: [], + h3: [], + h4: [], + h5: [], + h6: [], + i: [], + img: ['src', 'alt', 'title', 'width', 'height'], + li: [], + ol: [], + p: [], + pre: [], + s: [], + small: [], + span: [], + sub: [], + sup: [], + strong: [], + u: [], + ul: [] + /** + * A pattern that recognizes a commonly useful subset of URLs that are safe. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + + }; + var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi; + /** + * A pattern that matches safe data URLs. Only matches image, video and audio types. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + + var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i; + + function allowedAttribute(attr, allowedAttributeList) { + var attrName = attr.nodeName.toLowerCase(); + + if (allowedAttributeList.indexOf(attrName) !== -1) { + if (uriAttrs.indexOf(attrName) !== -1) { + return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN)); + } + + return true; + } + + var regExp = allowedAttributeList.filter(function (attrRegex) { + return attrRegex instanceof RegExp; + }); // Check if a regular expression validates the attribute. + + for (var i = 0, l = regExp.length; i < l; i++) { + if (attrName.match(regExp[i])) { + return true; + } + } + + return false; + } + + function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) { + if (unsafeHtml.length === 0) { + return unsafeHtml; + } + + if (sanitizeFn && typeof sanitizeFn === 'function') { + return sanitizeFn(unsafeHtml); + } + + var domParser = new window.DOMParser(); + var createdDocument = domParser.parseFromString(unsafeHtml, 'text/html'); + var whitelistKeys = Object.keys(whiteList); + var elements = [].slice.call(createdDocument.body.querySelectorAll('*')); + + var _loop = function _loop(i, len) { + var el = elements[i]; + var elName = el.nodeName.toLowerCase(); + + if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) { + el.parentNode.removeChild(el); + return "continue"; + } + + var attributeList = [].slice.call(el.attributes); + var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []); + attributeList.forEach(function (attr) { + if (!allowedAttribute(attr, whitelistedAttributes)) { + el.removeAttribute(attr.nodeName); + } + }); + }; + + for (var i = 0, len = elements.length; i < len; i++) { + var _ret = _loop(i, len); + + if (_ret === "continue") continue; + } + + return createdDocument.body.innerHTML; + } + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$6 = 'tooltip'; + var VERSION$6 = '4.3.1'; + var DATA_KEY$6 = 'bs.tooltip'; + var EVENT_KEY$6 = "." + DATA_KEY$6; + var JQUERY_NO_CONFLICT$6 = $.fn[NAME$6]; + var CLASS_PREFIX = 'bs-tooltip'; + var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g'); + var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']; + var DefaultType$4 = { + animation: 'boolean', + template: 'string', + title: '(string|element|function)', + trigger: 'string', + delay: '(number|object)', + html: 'boolean', + selector: '(string|boolean)', + placement: '(string|function)', + offset: '(number|string|function)', + container: '(string|element|boolean)', + fallbackPlacement: '(string|array)', + boundary: '(string|element)', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + whiteList: 'object' + }; + var AttachmentMap$1 = { + AUTO: 'auto', + TOP: 'top', + RIGHT: 'right', + BOTTOM: 'bottom', + LEFT: 'left' + }; + var Default$4 = { + animation: true, + template: '', + trigger: 'hover focus', + title: '', + delay: 0, + html: false, + selector: false, + placement: 'top', + offset: 0, + container: false, + fallbackPlacement: 'flip', + boundary: 'scrollParent', + sanitize: true, + sanitizeFn: null, + whiteList: DefaultWhitelist + }; + var HoverState = { + SHOW: 'show', + OUT: 'out' + }; + var Event$6 = { + HIDE: "hide" + EVENT_KEY$6, + HIDDEN: "hidden" + EVENT_KEY$6, + SHOW: "show" + EVENT_KEY$6, + SHOWN: "shown" + EVENT_KEY$6, + INSERTED: "inserted" + EVENT_KEY$6, + CLICK: "click" + EVENT_KEY$6, + FOCUSIN: "focusin" + EVENT_KEY$6, + FOCUSOUT: "focusout" + EVENT_KEY$6, + MOUSEENTER: "mouseenter" + EVENT_KEY$6, + MOUSELEAVE: "mouseleave" + EVENT_KEY$6 + }; + var ClassName$6 = { + FADE: 'fade', + SHOW: 'show' + }; + var Selector$6 = { + TOOLTIP: '.tooltip', + TOOLTIP_INNER: '.tooltip-inner', + ARROW: '.arrow' + }; + var Trigger = { + HOVER: 'hover', + FOCUS: 'focus', + CLICK: 'click', + MANUAL: 'manual' + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Tooltip = + /*#__PURE__*/ + function () { + function Tooltip(element, config) { + /** + * Check for Popper dependency + * Popper - https://popper.js.org + */ + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s tooltips require Popper.js (https://popper.js.org/)'); + } // private + + + this._isEnabled = true; + this._timeout = 0; + this._hoverState = ''; + this._activeTrigger = {}; + this._popper = null; // Protected + + this.element = element; + this.config = this._getConfig(config); + this.tip = null; + + this._setListeners(); + } // Getters + + + var _proto = Tooltip.prototype; + + // Public + _proto.enable = function enable() { + this._isEnabled = true; + }; + + _proto.disable = function disable() { + this._isEnabled = false; + }; + + _proto.toggleEnabled = function toggleEnabled() { + this._isEnabled = !this._isEnabled; + }; + + _proto.toggle = function toggle(event) { + if (!this._isEnabled) { + return; + } + + if (event) { + var dataKey = this.constructor.DATA_KEY; + var context = $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + context._activeTrigger.click = !context._activeTrigger.click; + + if (context._isWithActiveTrigger()) { + context._enter(null, context); + } else { + context._leave(null, context); + } + } else { + if ($(this.getTipElement()).hasClass(ClassName$6.SHOW)) { + this._leave(null, this); + + return; + } + + this._enter(null, this); + } + }; + + _proto.dispose = function dispose() { + clearTimeout(this._timeout); + $.removeData(this.element, this.constructor.DATA_KEY); + $(this.element).off(this.constructor.EVENT_KEY); + $(this.element).closest('.modal').off('hide.bs.modal'); + + if (this.tip) { + $(this.tip).remove(); + } + + this._isEnabled = null; + this._timeout = null; + this._hoverState = null; + this._activeTrigger = null; + + if (this._popper !== null) { + this._popper.destroy(); + } + + this._popper = null; + this.element = null; + this.config = null; + this.tip = null; + }; + + _proto.show = function show() { + var _this = this; + + if ($(this.element).css('display') === 'none') { + throw new Error('Please use show on visible elements'); + } + + var showEvent = $.Event(this.constructor.Event.SHOW); + + if (this.isWithContent() && this._isEnabled) { + $(this.element).trigger(showEvent); + var shadowRoot = Util.findShadowRoot(this.element); + var isInTheDom = $.contains(shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, this.element); + + if (showEvent.isDefaultPrevented() || !isInTheDom) { + return; + } + + var tip = this.getTipElement(); + var tipId = Util.getUID(this.constructor.NAME); + tip.setAttribute('id', tipId); + this.element.setAttribute('aria-describedby', tipId); + this.setContent(); + + if (this.config.animation) { + $(tip).addClass(ClassName$6.FADE); + } + + var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement; + + var attachment = this._getAttachment(placement); + + this.addAttachmentClass(attachment); + + var container = this._getContainer(); + + $(tip).data(this.constructor.DATA_KEY, this); + + if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) { + $(tip).appendTo(container); + } + + $(this.element).trigger(this.constructor.Event.INSERTED); + this._popper = new Popper(this.element, tip, { + placement: attachment, + modifiers: { + offset: this._getOffset(), + flip: { + behavior: this.config.fallbackPlacement + }, + arrow: { + element: Selector$6.ARROW + }, + preventOverflow: { + boundariesElement: this.config.boundary + } + }, + onCreate: function onCreate(data) { + if (data.originalPlacement !== data.placement) { + _this._handlePopperPlacementChange(data); + } + }, + onUpdate: function onUpdate(data) { + return _this._handlePopperPlacementChange(data); + } + }); + $(tip).addClass(ClassName$6.SHOW); // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().on('mouseover', null, $.noop); + } + + var complete = function complete() { + if (_this.config.animation) { + _this._fixTransition(); + } + + var prevHoverState = _this._hoverState; + _this._hoverState = null; + $(_this.element).trigger(_this.constructor.Event.SHOWN); + + if (prevHoverState === HoverState.OUT) { + _this._leave(null, _this); + } + }; + + if ($(this.tip).hasClass(ClassName$6.FADE)) { + var transitionDuration = Util.getTransitionDurationFromElement(this.tip); + $(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + } else { + complete(); + } + } + }; + + _proto.hide = function hide(callback) { + var _this2 = this; + + var tip = this.getTipElement(); + var hideEvent = $.Event(this.constructor.Event.HIDE); + + var complete = function complete() { + if (_this2._hoverState !== HoverState.SHOW && tip.parentNode) { + tip.parentNode.removeChild(tip); + } + + _this2._cleanTipClass(); + + _this2.element.removeAttribute('aria-describedby'); + + $(_this2.element).trigger(_this2.constructor.Event.HIDDEN); + + if (_this2._popper !== null) { + _this2._popper.destroy(); + } + + if (callback) { + callback(); + } + }; + + $(this.element).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + return; + } + + $(tip).removeClass(ClassName$6.SHOW); // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().off('mouseover', null, $.noop); + } + + this._activeTrigger[Trigger.CLICK] = false; + this._activeTrigger[Trigger.FOCUS] = false; + this._activeTrigger[Trigger.HOVER] = false; + + if ($(this.tip).hasClass(ClassName$6.FADE)) { + var transitionDuration = Util.getTransitionDurationFromElement(tip); + $(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + } else { + complete(); + } + + this._hoverState = ''; + }; + + _proto.update = function update() { + if (this._popper !== null) { + this._popper.scheduleUpdate(); + } + } // Protected + ; + + _proto.isWithContent = function isWithContent() { + return Boolean(this.getTitle()); + }; + + _proto.addAttachmentClass = function addAttachmentClass(attachment) { + $(this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment); + }; + + _proto.getTipElement = function getTipElement() { + this.tip = this.tip || $(this.config.template)[0]; + return this.tip; + }; + + _proto.setContent = function setContent() { + var tip = this.getTipElement(); + this.setElementContent($(tip.querySelectorAll(Selector$6.TOOLTIP_INNER)), this.getTitle()); + $(tip).removeClass(ClassName$6.FADE + " " + ClassName$6.SHOW); + }; + + _proto.setElementContent = function setElementContent($element, content) { + if (typeof content === 'object' && (content.nodeType || content.jquery)) { + // Content is a DOM node or a jQuery + if (this.config.html) { + if (!$(content).parent().is($element)) { + $element.empty().append(content); + } + } else { + $element.text($(content).text()); + } + + return; + } + + if (this.config.html) { + if (this.config.sanitize) { + content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn); + } + + $element.html(content); + } else { + $element.text(content); + } + }; + + _proto.getTitle = function getTitle() { + var title = this.element.getAttribute('data-original-title'); + + if (!title) { + title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title; + } + + return title; + } // Private + ; + + _proto._getOffset = function _getOffset() { + var _this3 = this; + + var offset = {}; + + if (typeof this.config.offset === 'function') { + offset.fn = function (data) { + data.offsets = _objectSpread({}, data.offsets, _this3.config.offset(data.offsets, _this3.element) || {}); + return data; + }; + } else { + offset.offset = this.config.offset; + } + + return offset; + }; + + _proto._getContainer = function _getContainer() { + if (this.config.container === false) { + return document.body; + } + + if (Util.isElement(this.config.container)) { + return $(this.config.container); + } + + return $(document).find(this.config.container); + }; + + _proto._getAttachment = function _getAttachment(placement) { + return AttachmentMap$1[placement.toUpperCase()]; + }; + + _proto._setListeners = function _setListeners() { + var _this4 = this; + + var triggers = this.config.trigger.split(' '); + triggers.forEach(function (trigger) { + if (trigger === 'click') { + $(_this4.element).on(_this4.constructor.Event.CLICK, _this4.config.selector, function (event) { + return _this4.toggle(event); + }); + } else if (trigger !== Trigger.MANUAL) { + var eventIn = trigger === Trigger.HOVER ? _this4.constructor.Event.MOUSEENTER : _this4.constructor.Event.FOCUSIN; + var eventOut = trigger === Trigger.HOVER ? _this4.constructor.Event.MOUSELEAVE : _this4.constructor.Event.FOCUSOUT; + $(_this4.element).on(eventIn, _this4.config.selector, function (event) { + return _this4._enter(event); + }).on(eventOut, _this4.config.selector, function (event) { + return _this4._leave(event); + }); + } + }); + $(this.element).closest('.modal').on('hide.bs.modal', function () { + if (_this4.element) { + _this4.hide(); + } + }); + + if (this.config.selector) { + this.config = _objectSpread({}, this.config, { + trigger: 'manual', + selector: '' + }); + } else { + this._fixTitle(); + } + }; + + _proto._fixTitle = function _fixTitle() { + var titleType = typeof this.element.getAttribute('data-original-title'); + + if (this.element.getAttribute('title') || titleType !== 'string') { + this.element.setAttribute('data-original-title', this.element.getAttribute('title') || ''); + this.element.setAttribute('title', ''); + } + }; + + _proto._enter = function _enter(event, context) { + var dataKey = this.constructor.DATA_KEY; + context = context || $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + if (event) { + context._activeTrigger[event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER] = true; + } + + if ($(context.getTipElement()).hasClass(ClassName$6.SHOW) || context._hoverState === HoverState.SHOW) { + context._hoverState = HoverState.SHOW; + return; + } + + clearTimeout(context._timeout); + context._hoverState = HoverState.SHOW; + + if (!context.config.delay || !context.config.delay.show) { + context.show(); + return; + } + + context._timeout = setTimeout(function () { + if (context._hoverState === HoverState.SHOW) { + context.show(); + } + }, context.config.delay.show); + }; + + _proto._leave = function _leave(event, context) { + var dataKey = this.constructor.DATA_KEY; + context = context || $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + if (event) { + context._activeTrigger[event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER] = false; + } + + if (context._isWithActiveTrigger()) { + return; + } + + clearTimeout(context._timeout); + context._hoverState = HoverState.OUT; + + if (!context.config.delay || !context.config.delay.hide) { + context.hide(); + return; + } + + context._timeout = setTimeout(function () { + if (context._hoverState === HoverState.OUT) { + context.hide(); + } + }, context.config.delay.hide); + }; + + _proto._isWithActiveTrigger = function _isWithActiveTrigger() { + for (var trigger in this._activeTrigger) { + if (this._activeTrigger[trigger]) { + return true; + } + } + + return false; + }; + + _proto._getConfig = function _getConfig(config) { + var dataAttributes = $(this.element).data(); + Object.keys(dataAttributes).forEach(function (dataAttr) { + if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) { + delete dataAttributes[dataAttr]; + } + }); + config = _objectSpread({}, this.constructor.Default, dataAttributes, typeof config === 'object' && config ? config : {}); + + if (typeof config.delay === 'number') { + config.delay = { + show: config.delay, + hide: config.delay + }; + } + + if (typeof config.title === 'number') { + config.title = config.title.toString(); + } + + if (typeof config.content === 'number') { + config.content = config.content.toString(); + } + + Util.typeCheckConfig(NAME$6, config, this.constructor.DefaultType); + + if (config.sanitize) { + config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn); + } + + return config; + }; + + _proto._getDelegateConfig = function _getDelegateConfig() { + var config = {}; + + if (this.config) { + for (var key in this.config) { + if (this.constructor.Default[key] !== this.config[key]) { + config[key] = this.config[key]; + } + } + } + + return config; + }; + + _proto._cleanTipClass = function _cleanTipClass() { + var $tip = $(this.getTipElement()); + var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX); + + if (tabClass !== null && tabClass.length) { + $tip.removeClass(tabClass.join('')); + } + }; + + _proto._handlePopperPlacementChange = function _handlePopperPlacementChange(popperData) { + var popperInstance = popperData.instance; + this.tip = popperInstance.popper; + + this._cleanTipClass(); + + this.addAttachmentClass(this._getAttachment(popperData.placement)); + }; + + _proto._fixTransition = function _fixTransition() { + var tip = this.getTipElement(); + var initConfigAnimation = this.config.animation; + + if (tip.getAttribute('x-placement') !== null) { + return; + } + + $(tip).removeClass(ClassName$6.FADE); + this.config.animation = false; + this.hide(); + this.show(); + this.config.animation = initConfigAnimation; + } // Static + ; + + Tooltip._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$6); + + var _config = typeof config === 'object' && config; + + if (!data && /dispose|hide/.test(config)) { + return; + } + + if (!data) { + data = new Tooltip(this, _config); + $(this).data(DATA_KEY$6, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Tooltip, null, [{ + key: "VERSION", + get: function get() { + return VERSION$6; + } + }, { + key: "Default", + get: function get() { + return Default$4; + } + }, { + key: "NAME", + get: function get() { + return NAME$6; + } + }, { + key: "DATA_KEY", + get: function get() { + return DATA_KEY$6; + } + }, { + key: "Event", + get: function get() { + return Event$6; + } + }, { + key: "EVENT_KEY", + get: function get() { + return EVENT_KEY$6; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$4; + } + }]); + + return Tooltip; + }(); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + + $.fn[NAME$6] = Tooltip._jQueryInterface; + $.fn[NAME$6].Constructor = Tooltip; + + $.fn[NAME$6].noConflict = function () { + $.fn[NAME$6] = JQUERY_NO_CONFLICT$6; + return Tooltip._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$7 = 'popover'; + var VERSION$7 = '4.3.1'; + var DATA_KEY$7 = 'bs.popover'; + var EVENT_KEY$7 = "." + DATA_KEY$7; + var JQUERY_NO_CONFLICT$7 = $.fn[NAME$7]; + var CLASS_PREFIX$1 = 'bs-popover'; + var BSCLS_PREFIX_REGEX$1 = new RegExp("(^|\\s)" + CLASS_PREFIX$1 + "\\S+", 'g'); + + var Default$5 = _objectSpread({}, Tooltip.Default, { + placement: 'right', + trigger: 'click', + content: '', + template: '' + }); + + var DefaultType$5 = _objectSpread({}, Tooltip.DefaultType, { + content: '(string|element|function)' + }); + + var ClassName$7 = { + FADE: 'fade', + SHOW: 'show' + }; + var Selector$7 = { + TITLE: '.popover-header', + CONTENT: '.popover-body' + }; + var Event$7 = { + HIDE: "hide" + EVENT_KEY$7, + HIDDEN: "hidden" + EVENT_KEY$7, + SHOW: "show" + EVENT_KEY$7, + SHOWN: "shown" + EVENT_KEY$7, + INSERTED: "inserted" + EVENT_KEY$7, + CLICK: "click" + EVENT_KEY$7, + FOCUSIN: "focusin" + EVENT_KEY$7, + FOCUSOUT: "focusout" + EVENT_KEY$7, + MOUSEENTER: "mouseenter" + EVENT_KEY$7, + MOUSELEAVE: "mouseleave" + EVENT_KEY$7 + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Popover = + /*#__PURE__*/ + function (_Tooltip) { + _inheritsLoose(Popover, _Tooltip); + + function Popover() { + return _Tooltip.apply(this, arguments) || this; + } + + var _proto = Popover.prototype; + + // Overrides + _proto.isWithContent = function isWithContent() { + return this.getTitle() || this._getContent(); + }; + + _proto.addAttachmentClass = function addAttachmentClass(attachment) { + $(this.getTipElement()).addClass(CLASS_PREFIX$1 + "-" + attachment); + }; + + _proto.getTipElement = function getTipElement() { + this.tip = this.tip || $(this.config.template)[0]; + return this.tip; + }; + + _proto.setContent = function setContent() { + var $tip = $(this.getTipElement()); // We use append for html objects to maintain js events + + this.setElementContent($tip.find(Selector$7.TITLE), this.getTitle()); + + var content = this._getContent(); + + if (typeof content === 'function') { + content = content.call(this.element); + } + + this.setElementContent($tip.find(Selector$7.CONTENT), content); + $tip.removeClass(ClassName$7.FADE + " " + ClassName$7.SHOW); + } // Private + ; + + _proto._getContent = function _getContent() { + return this.element.getAttribute('data-content') || this.config.content; + }; + + _proto._cleanTipClass = function _cleanTipClass() { + var $tip = $(this.getTipElement()); + var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX$1); + + if (tabClass !== null && tabClass.length > 0) { + $tip.removeClass(tabClass.join('')); + } + } // Static + ; + + Popover._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$7); + + var _config = typeof config === 'object' ? config : null; + + if (!data && /dispose|hide/.test(config)) { + return; + } + + if (!data) { + data = new Popover(this, _config); + $(this).data(DATA_KEY$7, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Popover, null, [{ + key: "VERSION", + // Getters + get: function get() { + return VERSION$7; + } + }, { + key: "Default", + get: function get() { + return Default$5; + } + }, { + key: "NAME", + get: function get() { + return NAME$7; + } + }, { + key: "DATA_KEY", + get: function get() { + return DATA_KEY$7; + } + }, { + key: "Event", + get: function get() { + return Event$7; + } + }, { + key: "EVENT_KEY", + get: function get() { + return EVENT_KEY$7; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$5; + } + }]); + + return Popover; + }(Tooltip); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + + $.fn[NAME$7] = Popover._jQueryInterface; + $.fn[NAME$7].Constructor = Popover; + + $.fn[NAME$7].noConflict = function () { + $.fn[NAME$7] = JQUERY_NO_CONFLICT$7; + return Popover._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$8 = 'scrollspy'; + var VERSION$8 = '4.3.1'; + var DATA_KEY$8 = 'bs.scrollspy'; + var EVENT_KEY$8 = "." + DATA_KEY$8; + var DATA_API_KEY$6 = '.data-api'; + var JQUERY_NO_CONFLICT$8 = $.fn[NAME$8]; + var Default$6 = { + offset: 10, + method: 'auto', + target: '' + }; + var DefaultType$6 = { + offset: 'number', + method: 'string', + target: '(string|element)' + }; + var Event$8 = { + ACTIVATE: "activate" + EVENT_KEY$8, + SCROLL: "scroll" + EVENT_KEY$8, + LOAD_DATA_API: "load" + EVENT_KEY$8 + DATA_API_KEY$6 + }; + var ClassName$8 = { + DROPDOWN_ITEM: 'dropdown-item', + DROPDOWN_MENU: 'dropdown-menu', + ACTIVE: 'active' + }; + var Selector$8 = { + DATA_SPY: '[data-spy="scroll"]', + ACTIVE: '.active', + NAV_LIST_GROUP: '.nav, .list-group', + NAV_LINKS: '.nav-link', + NAV_ITEMS: '.nav-item', + LIST_ITEMS: '.list-group-item', + DROPDOWN: '.dropdown', + DROPDOWN_ITEMS: '.dropdown-item', + DROPDOWN_TOGGLE: '.dropdown-toggle' + }; + var OffsetMethod = { + OFFSET: 'offset', + POSITION: 'position' + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var ScrollSpy = + /*#__PURE__*/ + function () { + function ScrollSpy(element, config) { + var _this = this; + + this._element = element; + this._scrollElement = element.tagName === 'BODY' ? window : element; + this._config = this._getConfig(config); + this._selector = this._config.target + " " + Selector$8.NAV_LINKS + "," + (this._config.target + " " + Selector$8.LIST_ITEMS + ",") + (this._config.target + " " + Selector$8.DROPDOWN_ITEMS); + this._offsets = []; + this._targets = []; + this._activeTarget = null; + this._scrollHeight = 0; + $(this._scrollElement).on(Event$8.SCROLL, function (event) { + return _this._process(event); + }); + this.refresh(); + + this._process(); + } // Getters + + + var _proto = ScrollSpy.prototype; + + // Public + _proto.refresh = function refresh() { + var _this2 = this; + + var autoMethod = this._scrollElement === this._scrollElement.window ? OffsetMethod.OFFSET : OffsetMethod.POSITION; + var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method; + var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0; + this._offsets = []; + this._targets = []; + this._scrollHeight = this._getScrollHeight(); + var targets = [].slice.call(document.querySelectorAll(this._selector)); + targets.map(function (element) { + var target; + var targetSelector = Util.getSelectorFromElement(element); + + if (targetSelector) { + target = document.querySelector(targetSelector); + } + + if (target) { + var targetBCR = target.getBoundingClientRect(); + + if (targetBCR.width || targetBCR.height) { + // TODO (fat): remove sketch reliance on jQuery position/offset + return [$(target)[offsetMethod]().top + offsetBase, targetSelector]; + } + } + + return null; + }).filter(function (item) { + return item; + }).sort(function (a, b) { + return a[0] - b[0]; + }).forEach(function (item) { + _this2._offsets.push(item[0]); + + _this2._targets.push(item[1]); + }); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$8); + $(this._scrollElement).off(EVENT_KEY$8); + this._element = null; + this._scrollElement = null; + this._config = null; + this._selector = null; + this._offsets = null; + this._targets = null; + this._activeTarget = null; + this._scrollHeight = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread({}, Default$6, typeof config === 'object' && config ? config : {}); + + if (typeof config.target !== 'string') { + var id = $(config.target).attr('id'); + + if (!id) { + id = Util.getUID(NAME$8); + $(config.target).attr('id', id); + } + + config.target = "#" + id; + } + + Util.typeCheckConfig(NAME$8, config, DefaultType$6); + return config; + }; + + _proto._getScrollTop = function _getScrollTop() { + return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop; + }; + + _proto._getScrollHeight = function _getScrollHeight() { + return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); + }; + + _proto._getOffsetHeight = function _getOffsetHeight() { + return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height; + }; + + _proto._process = function _process() { + var scrollTop = this._getScrollTop() + this._config.offset; + + var scrollHeight = this._getScrollHeight(); + + var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight(); + + if (this._scrollHeight !== scrollHeight) { + this.refresh(); + } + + if (scrollTop >= maxScroll) { + var target = this._targets[this._targets.length - 1]; + + if (this._activeTarget !== target) { + this._activate(target); + } + + return; + } + + if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) { + this._activeTarget = null; + + this._clear(); + + return; + } + + var offsetLength = this._offsets.length; + + for (var i = offsetLength; i--;) { + var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]); + + if (isActiveTarget) { + this._activate(this._targets[i]); + } + } + }; + + _proto._activate = function _activate(target) { + this._activeTarget = target; + + this._clear(); + + var queries = this._selector.split(',').map(function (selector) { + return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]"; + }); + + var $link = $([].slice.call(document.querySelectorAll(queries.join(',')))); + + if ($link.hasClass(ClassName$8.DROPDOWN_ITEM)) { + $link.closest(Selector$8.DROPDOWN).find(Selector$8.DROPDOWN_TOGGLE).addClass(ClassName$8.ACTIVE); + $link.addClass(ClassName$8.ACTIVE); + } else { + // Set triggered link as active + $link.addClass(ClassName$8.ACTIVE); // Set triggered links parents as active + // With both